diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000000..3335045086 --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE.txt similarity index 92% rename from LICENSE rename to LICENSE.txt index 70566f2d0e..e587591e14 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. @@ -618,57 +618,4 @@ an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/assets/amulet.png b/assets/amulet.png new file mode 100644 index 0000000000..8ebbac7ccf Binary files /dev/null and b/assets/amulet.png differ diff --git a/assets/arcs1.png b/assets/arcs1.png new file mode 100644 index 0000000000..900b52e137 Binary files /dev/null and b/assets/arcs1.png differ diff --git a/assets/arcs2.png b/assets/arcs2.png new file mode 100644 index 0000000000..1c57482827 Binary files /dev/null and b/assets/arcs2.png differ diff --git a/assets/avatars.png b/assets/avatars.png new file mode 100644 index 0000000000..3bd332c5ab Binary files /dev/null and b/assets/avatars.png differ diff --git a/assets/badges.png b/assets/badges.png new file mode 100644 index 0000000000..20d380331b Binary files /dev/null and b/assets/badges.png differ diff --git a/assets/banners.png b/assets/banners.png new file mode 100644 index 0000000000..b0a01b7871 Binary files /dev/null and b/assets/banners.png differ diff --git a/assets/bat.png b/assets/bat.png new file mode 100644 index 0000000000..d1104c422c Binary files /dev/null and b/assets/bat.png differ diff --git a/assets/blacksmith.png b/assets/blacksmith.png new file mode 100644 index 0000000000..d5e1fd91f8 Binary files /dev/null and b/assets/blacksmith.png differ diff --git a/assets/brute.png b/assets/brute.png new file mode 100644 index 0000000000..e014438363 Binary files /dev/null and b/assets/brute.png differ diff --git a/assets/buffs.png b/assets/buffs.png new file mode 100644 index 0000000000..7c12ab545d Binary files /dev/null and b/assets/buffs.png differ diff --git a/assets/burning_fist.png b/assets/burning_fist.png new file mode 100644 index 0000000000..ea34c58c06 Binary files /dev/null and b/assets/burning_fist.png differ diff --git a/assets/chrome.png b/assets/chrome.png new file mode 100644 index 0000000000..e11048efa1 Binary files /dev/null and b/assets/chrome.png differ diff --git a/assets/crab.png b/assets/crab.png new file mode 100644 index 0000000000..f80950592d Binary files /dev/null and b/assets/crab.png differ diff --git a/assets/dashboard.png b/assets/dashboard.png new file mode 100644 index 0000000000..9cd503cd53 Binary files /dev/null and b/assets/dashboard.png differ diff --git a/assets/demon.png b/assets/demon.png new file mode 100644 index 0000000000..876599b478 Binary files /dev/null and b/assets/demon.png differ diff --git a/assets/dm300.png b/assets/dm300.png new file mode 100644 index 0000000000..6a1d68495a Binary files /dev/null and b/assets/dm300.png differ diff --git a/assets/effects.png b/assets/effects.png new file mode 100644 index 0000000000..2df82bf91c Binary files /dev/null and b/assets/effects.png differ diff --git a/assets/elemental.png b/assets/elemental.png new file mode 100644 index 0000000000..d0090492b6 Binary files /dev/null and b/assets/elemental.png differ diff --git a/assets/exp_bar.png b/assets/exp_bar.png new file mode 100644 index 0000000000..81825389f9 Binary files /dev/null and b/assets/exp_bar.png differ diff --git a/assets/eye.png b/assets/eye.png new file mode 100644 index 0000000000..5c2487350f Binary files /dev/null and b/assets/eye.png differ diff --git a/assets/fireball.png b/assets/fireball.png new file mode 100644 index 0000000000..0ab9b1a3af Binary files /dev/null and b/assets/fireball.png differ diff --git a/assets/font15x.png b/assets/font15x.png new file mode 100644 index 0000000000..2e0d7320be Binary files /dev/null and b/assets/font15x.png differ diff --git a/assets/font1x.png b/assets/font1x.png new file mode 100644 index 0000000000..7106aafc13 Binary files /dev/null and b/assets/font1x.png differ diff --git a/assets/font25x.png b/assets/font25x.png new file mode 100644 index 0000000000..d39d4cf60c Binary files /dev/null and b/assets/font25x.png differ diff --git a/assets/font2x.png b/assets/font2x.png new file mode 100644 index 0000000000..adc6f614b3 Binary files /dev/null and b/assets/font2x.png differ diff --git a/assets/font3x.png b/assets/font3x.png new file mode 100644 index 0000000000..87011cdda8 Binary files /dev/null and b/assets/font3x.png differ diff --git a/assets/game.mp3 b/assets/game.mp3 new file mode 100644 index 0000000000..5306460fbd Binary files /dev/null and b/assets/game.mp3 differ diff --git a/assets/ghost.png b/assets/ghost.png new file mode 100644 index 0000000000..a9ffadbcde Binary files /dev/null and b/assets/ghost.png differ diff --git a/assets/gnoll.png b/assets/gnoll.png new file mode 100644 index 0000000000..a4c614f1b2 Binary files /dev/null and b/assets/gnoll.png differ diff --git a/assets/golem.png b/assets/golem.png new file mode 100644 index 0000000000..e9e25d0fe3 Binary files /dev/null and b/assets/golem.png differ diff --git a/assets/goo.png b/assets/goo.png new file mode 100644 index 0000000000..5e07a35a85 Binary files /dev/null and b/assets/goo.png differ diff --git a/assets/hp_bar.png b/assets/hp_bar.png new file mode 100644 index 0000000000..fa3197e073 Binary files /dev/null and b/assets/hp_bar.png differ diff --git a/assets/icons.png b/assets/icons.png new file mode 100644 index 0000000000..608d735c79 Binary files /dev/null and b/assets/icons.png differ diff --git a/assets/items.png b/assets/items.png new file mode 100644 index 0000000000..c758c8c5c5 Binary files /dev/null and b/assets/items.png differ diff --git a/assets/king.png b/assets/king.png new file mode 100644 index 0000000000..12c58e7b6d Binary files /dev/null and b/assets/king.png differ diff --git a/assets/large_buffs.png b/assets/large_buffs.png new file mode 100644 index 0000000000..42cd34ff87 Binary files /dev/null and b/assets/large_buffs.png differ diff --git a/assets/larva.png b/assets/larva.png new file mode 100644 index 0000000000..0a73364eb7 Binary files /dev/null and b/assets/larva.png differ diff --git a/assets/mage.png b/assets/mage.png new file mode 100644 index 0000000000..74a41b9abf Binary files /dev/null and b/assets/mage.png differ diff --git a/assets/monk.png b/assets/monk.png new file mode 100644 index 0000000000..8cf78e4ee1 Binary files /dev/null and b/assets/monk.png differ diff --git a/assets/piranha.png b/assets/piranha.png new file mode 100644 index 0000000000..3865e624ed Binary files /dev/null and b/assets/piranha.png differ diff --git a/assets/plants.png b/assets/plants.png new file mode 100644 index 0000000000..72604346b0 Binary files /dev/null and b/assets/plants.png differ diff --git a/assets/ranger.png b/assets/ranger.png new file mode 100644 index 0000000000..56175742b1 Binary files /dev/null and b/assets/ranger.png differ diff --git a/assets/rat.png b/assets/rat.png new file mode 100644 index 0000000000..4b8372fd47 Binary files /dev/null and b/assets/rat.png differ diff --git a/assets/ratking.png b/assets/ratking.png new file mode 100644 index 0000000000..d39b944534 Binary files /dev/null and b/assets/ratking.png differ diff --git a/assets/rogue.png b/assets/rogue.png new file mode 100644 index 0000000000..42eafb07bf Binary files /dev/null and b/assets/rogue.png differ diff --git a/assets/rotting_fist.png b/assets/rotting_fist.png new file mode 100644 index 0000000000..146a57a943 Binary files /dev/null and b/assets/rotting_fist.png differ diff --git a/assets/scorpio.png b/assets/scorpio.png new file mode 100644 index 0000000000..dc1184e515 Binary files /dev/null and b/assets/scorpio.png differ diff --git a/assets/shaman.png b/assets/shaman.png new file mode 100644 index 0000000000..fbc101ed08 Binary files /dev/null and b/assets/shaman.png differ diff --git a/assets/sheep.png b/assets/sheep.png new file mode 100644 index 0000000000..ab9f37be9b Binary files /dev/null and b/assets/sheep.png differ diff --git a/assets/shopkeeper.png b/assets/shopkeeper.png new file mode 100644 index 0000000000..6b42e91adb Binary files /dev/null and b/assets/shopkeeper.png differ diff --git a/assets/skeleton.png b/assets/skeleton.png new file mode 100644 index 0000000000..8c70a2094f Binary files /dev/null and b/assets/skeleton.png differ diff --git a/assets/snd_alert.mp3 b/assets/snd_alert.mp3 new file mode 100644 index 0000000000..c6af7803e1 Binary files /dev/null and b/assets/snd_alert.mp3 differ diff --git a/assets/snd_badge.mp3 b/assets/snd_badge.mp3 new file mode 100644 index 0000000000..c022ccd857 Binary files /dev/null and b/assets/snd_badge.mp3 differ diff --git a/assets/snd_beacon.mp3 b/assets/snd_beacon.mp3 new file mode 100644 index 0000000000..9578863561 Binary files /dev/null and b/assets/snd_beacon.mp3 differ diff --git a/assets/snd_blast.mp3 b/assets/snd_blast.mp3 new file mode 100644 index 0000000000..bd8a2ff742 Binary files /dev/null and b/assets/snd_blast.mp3 differ diff --git a/assets/snd_bones.mp3 b/assets/snd_bones.mp3 new file mode 100644 index 0000000000..7ec6f0a373 Binary files /dev/null and b/assets/snd_bones.mp3 differ diff --git a/assets/snd_boss.mp3 b/assets/snd_boss.mp3 new file mode 100644 index 0000000000..942bf3deaf Binary files /dev/null and b/assets/snd_boss.mp3 differ diff --git a/assets/snd_burning.mp3 b/assets/snd_burning.mp3 new file mode 100644 index 0000000000..c45ea9f397 Binary files /dev/null and b/assets/snd_burning.mp3 differ diff --git a/assets/snd_challenge.mp3 b/assets/snd_challenge.mp3 new file mode 100644 index 0000000000..f014020fce Binary files /dev/null and b/assets/snd_challenge.mp3 differ diff --git a/assets/snd_charms.mp3 b/assets/snd_charms.mp3 new file mode 100644 index 0000000000..3212caf6ce Binary files /dev/null and b/assets/snd_charms.mp3 differ diff --git a/assets/snd_click.mp3 b/assets/snd_click.mp3 new file mode 100644 index 0000000000..5c0728e5e6 Binary files /dev/null and b/assets/snd_click.mp3 differ diff --git a/assets/snd_cursed.mp3 b/assets/snd_cursed.mp3 new file mode 100644 index 0000000000..74351909cb Binary files /dev/null and b/assets/snd_cursed.mp3 differ diff --git a/assets/snd_death.mp3 b/assets/snd_death.mp3 new file mode 100644 index 0000000000..9cae7c85ff Binary files /dev/null and b/assets/snd_death.mp3 differ diff --git a/assets/snd_descend.mp3 b/assets/snd_descend.mp3 new file mode 100644 index 0000000000..9ab56bccf1 Binary files /dev/null and b/assets/snd_descend.mp3 differ diff --git a/assets/snd_dewdrop.mp3 b/assets/snd_dewdrop.mp3 new file mode 100644 index 0000000000..23da344663 Binary files /dev/null and b/assets/snd_dewdrop.mp3 differ diff --git a/assets/snd_door_open.mp3 b/assets/snd_door_open.mp3 new file mode 100644 index 0000000000..c1b5ba8701 Binary files /dev/null and b/assets/snd_door_open.mp3 differ diff --git a/assets/snd_drink.mp3 b/assets/snd_drink.mp3 new file mode 100644 index 0000000000..5b86710e7c Binary files /dev/null and b/assets/snd_drink.mp3 differ diff --git a/assets/snd_eat.mp3 b/assets/snd_eat.mp3 new file mode 100644 index 0000000000..9a02829ed7 Binary files /dev/null and b/assets/snd_eat.mp3 differ diff --git a/assets/snd_evoke.mp3 b/assets/snd_evoke.mp3 new file mode 100644 index 0000000000..f3592a97cc Binary files /dev/null and b/assets/snd_evoke.mp3 differ diff --git a/assets/snd_falling.mp3 b/assets/snd_falling.mp3 new file mode 100644 index 0000000000..51a1898170 Binary files /dev/null and b/assets/snd_falling.mp3 differ diff --git a/assets/snd_ghost.mp3 b/assets/snd_ghost.mp3 new file mode 100644 index 0000000000..dc1e5560bf Binary files /dev/null and b/assets/snd_ghost.mp3 differ diff --git a/assets/snd_gold.mp3 b/assets/snd_gold.mp3 new file mode 100644 index 0000000000..f3b134be46 Binary files /dev/null and b/assets/snd_gold.mp3 differ diff --git a/assets/snd_hit.mp3 b/assets/snd_hit.mp3 new file mode 100644 index 0000000000..d122b684b6 Binary files /dev/null and b/assets/snd_hit.mp3 differ diff --git a/assets/snd_item.mp3 b/assets/snd_item.mp3 new file mode 100644 index 0000000000..dc67e92a6f Binary files /dev/null and b/assets/snd_item.mp3 differ diff --git a/assets/snd_levelup.mp3 b/assets/snd_levelup.mp3 new file mode 100644 index 0000000000..9a995edcce Binary files /dev/null and b/assets/snd_levelup.mp3 differ diff --git a/assets/snd_lightning.mp3 b/assets/snd_lightning.mp3 new file mode 100644 index 0000000000..e072112f80 Binary files /dev/null and b/assets/snd_lightning.mp3 differ diff --git a/assets/snd_lullaby.mp3 b/assets/snd_lullaby.mp3 new file mode 100644 index 0000000000..a686a37d58 Binary files /dev/null and b/assets/snd_lullaby.mp3 differ diff --git a/assets/snd_mastery.mp3 b/assets/snd_mastery.mp3 new file mode 100644 index 0000000000..08e0727735 Binary files /dev/null and b/assets/snd_mastery.mp3 differ diff --git a/assets/snd_meld.mp3 b/assets/snd_meld.mp3 new file mode 100644 index 0000000000..723130feb7 Binary files /dev/null and b/assets/snd_meld.mp3 differ diff --git a/assets/snd_miss.mp3 b/assets/snd_miss.mp3 new file mode 100644 index 0000000000..58de0fdf54 Binary files /dev/null and b/assets/snd_miss.mp3 differ diff --git a/assets/snd_plant.mp3 b/assets/snd_plant.mp3 new file mode 100644 index 0000000000..d4b7e55a53 Binary files /dev/null and b/assets/snd_plant.mp3 differ diff --git a/assets/snd_puff.mp3 b/assets/snd_puff.mp3 new file mode 100644 index 0000000000..eb4274edeb Binary files /dev/null and b/assets/snd_puff.mp3 differ diff --git a/assets/snd_ray.mp3 b/assets/snd_ray.mp3 new file mode 100644 index 0000000000..ef5a21775b Binary files /dev/null and b/assets/snd_ray.mp3 differ diff --git a/assets/snd_read.mp3 b/assets/snd_read.mp3 new file mode 100644 index 0000000000..dd1ce822ee Binary files /dev/null and b/assets/snd_read.mp3 differ diff --git a/assets/snd_rocks.mp3 b/assets/snd_rocks.mp3 new file mode 100644 index 0000000000..1f94f57cef Binary files /dev/null and b/assets/snd_rocks.mp3 differ diff --git a/assets/snd_secret.mp3 b/assets/snd_secret.mp3 new file mode 100644 index 0000000000..934f9e60c4 Binary files /dev/null and b/assets/snd_secret.mp3 differ diff --git a/assets/snd_shatter.mp3 b/assets/snd_shatter.mp3 new file mode 100644 index 0000000000..33b500ff1c Binary files /dev/null and b/assets/snd_shatter.mp3 differ diff --git a/assets/snd_step.mp3 b/assets/snd_step.mp3 new file mode 100644 index 0000000000..fac28ea08e Binary files /dev/null and b/assets/snd_step.mp3 differ diff --git a/assets/snd_teleport.mp3 b/assets/snd_teleport.mp3 new file mode 100644 index 0000000000..cc42df593b Binary files /dev/null and b/assets/snd_teleport.mp3 differ diff --git a/assets/snd_tomb.mp3 b/assets/snd_tomb.mp3 new file mode 100644 index 0000000000..9a25e13bfd Binary files /dev/null and b/assets/snd_tomb.mp3 differ diff --git a/assets/snd_trap.mp3 b/assets/snd_trap.mp3 new file mode 100644 index 0000000000..f09f612f03 Binary files /dev/null and b/assets/snd_trap.mp3 differ diff --git a/assets/snd_unlock.mp3 b/assets/snd_unlock.mp3 new file mode 100644 index 0000000000..8adf540b90 Binary files /dev/null and b/assets/snd_unlock.mp3 differ diff --git a/assets/snd_water.mp3 b/assets/snd_water.mp3 new file mode 100644 index 0000000000..1e2310af85 Binary files /dev/null and b/assets/snd_water.mp3 differ diff --git a/assets/snd_zap.mp3 b/assets/snd_zap.mp3 new file mode 100644 index 0000000000..3c4e3dd49a Binary files /dev/null and b/assets/snd_zap.mp3 differ diff --git a/assets/specks.png b/assets/specks.png new file mode 100644 index 0000000000..1363636d99 Binary files /dev/null and b/assets/specks.png differ diff --git a/assets/spell_icons.png b/assets/spell_icons.png new file mode 100644 index 0000000000..81ffebbfb5 Binary files /dev/null and b/assets/spell_icons.png differ diff --git a/assets/spinner.png b/assets/spinner.png new file mode 100644 index 0000000000..2605e497fb Binary files /dev/null and b/assets/spinner.png differ diff --git a/assets/statue.png b/assets/statue.png new file mode 100644 index 0000000000..b8ad49022f Binary files /dev/null and b/assets/statue.png differ diff --git a/assets/status_pane.png b/assets/status_pane.png new file mode 100644 index 0000000000..8751f12d4b Binary files /dev/null and b/assets/status_pane.png differ diff --git a/assets/succubus.png b/assets/succubus.png new file mode 100644 index 0000000000..fc577df8cf Binary files /dev/null and b/assets/succubus.png differ diff --git a/assets/surface.mp3 b/assets/surface.mp3 new file mode 100644 index 0000000000..d172a29451 Binary files /dev/null and b/assets/surface.mp3 differ diff --git a/assets/surface.png b/assets/surface.png new file mode 100644 index 0000000000..75f9a644a9 Binary files /dev/null and b/assets/surface.png differ diff --git a/assets/swarm.png b/assets/swarm.png new file mode 100644 index 0000000000..c626735758 Binary files /dev/null and b/assets/swarm.png differ diff --git a/assets/tengu.png b/assets/tengu.png new file mode 100644 index 0000000000..407ae307e3 Binary files /dev/null and b/assets/tengu.png differ diff --git a/assets/theme.mp3 b/assets/theme.mp3 new file mode 100644 index 0000000000..acb7c7a62e Binary files /dev/null and b/assets/theme.mp3 differ diff --git a/assets/thief.png b/assets/thief.png new file mode 100644 index 0000000000..ecdb0b3c7b Binary files /dev/null and b/assets/thief.png differ diff --git a/assets/tiles0.png b/assets/tiles0.png new file mode 100644 index 0000000000..cc42fdfddc Binary files /dev/null and b/assets/tiles0.png differ diff --git a/assets/tiles1.png b/assets/tiles1.png new file mode 100644 index 0000000000..ecf5af50b5 Binary files /dev/null and b/assets/tiles1.png differ diff --git a/assets/tiles2.png b/assets/tiles2.png new file mode 100644 index 0000000000..aacab7c17d Binary files /dev/null and b/assets/tiles2.png differ diff --git a/assets/tiles3.png b/assets/tiles3.png new file mode 100644 index 0000000000..8fa5091ca7 Binary files /dev/null and b/assets/tiles3.png differ diff --git a/assets/tiles4.png b/assets/tiles4.png new file mode 100644 index 0000000000..fc6e63fc82 Binary files /dev/null and b/assets/tiles4.png differ diff --git a/assets/toolbar.png b/assets/toolbar.png new file mode 100644 index 0000000000..89fe80800d Binary files /dev/null and b/assets/toolbar.png differ diff --git a/assets/undead.png b/assets/undead.png new file mode 100644 index 0000000000..ac066eeea2 Binary files /dev/null and b/assets/undead.png differ diff --git a/assets/wandmaker.png b/assets/wandmaker.png new file mode 100644 index 0000000000..19e8fe3d67 Binary files /dev/null and b/assets/wandmaker.png differ diff --git a/assets/warlock.png b/assets/warlock.png new file mode 100644 index 0000000000..76b5874ce0 Binary files /dev/null and b/assets/warlock.png differ diff --git a/assets/warrior.png b/assets/warrior.png new file mode 100644 index 0000000000..a98ee5d3d9 Binary files /dev/null and b/assets/warrior.png differ diff --git a/assets/water0.png b/assets/water0.png new file mode 100644 index 0000000000..2f421efba8 Binary files /dev/null and b/assets/water0.png differ diff --git a/assets/water1.png b/assets/water1.png new file mode 100644 index 0000000000..95d61515ee Binary files /dev/null and b/assets/water1.png differ diff --git a/assets/water2.png b/assets/water2.png new file mode 100644 index 0000000000..ddef41dea2 Binary files /dev/null and b/assets/water2.png differ diff --git a/assets/water3.png b/assets/water3.png new file mode 100644 index 0000000000..e1646eba58 Binary files /dev/null and b/assets/water3.png differ diff --git a/assets/water4.png b/assets/water4.png new file mode 100644 index 0000000000..40b76ca495 Binary files /dev/null and b/assets/water4.png differ diff --git a/assets/wraith.png b/assets/wraith.png new file mode 100644 index 0000000000..d19a586253 Binary files /dev/null and b/assets/wraith.png differ diff --git a/assets/yog.png b/assets/yog.png new file mode 100644 index 0000000000..6665bea07a Binary files /dev/null and b/assets/yog.png differ diff --git a/proguard-project.txt b/proguard-project.txt new file mode 100644 index 0000000000..f2fe1559a2 --- /dev/null +++ b/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000..6d3d04f230 Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000000..747b8f4184 Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..ecd7e58330 Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cb74bf9617 Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/res/drawable-xxxhdpi/ic_launcher.png b/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..057fc672cc Binary files /dev/null and b/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml new file mode 100644 index 0000000000..3c02242ad0 --- /dev/null +++ b/res/values-v11/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml new file mode 100644 index 0000000000..a91fd0372b --- /dev/null +++ b/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000000..632a9f6ee9 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Pixel Dungeon + + \ No newline at end of file diff --git a/res/values/styles.xml b/res/values/styles.xml new file mode 100644 index 0000000000..6ce89c7ba4 --- /dev/null +++ b/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/src/com/watabou/pixeldungeon/Assets.java b/src/com/watabou/pixeldungeon/Assets.java new file mode 100644 index 0000000000..a940c77265 --- /dev/null +++ b/src/com/watabou/pixeldungeon/Assets.java @@ -0,0 +1,159 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +public class Assets { + + public static final String ARCS_BG = "arcs1.png"; + public static final String ARCS_FG = "arcs2.png"; + public static final String DASHBOARD = "dashboard.png"; + + public static final String BANNERS = "banners.png"; + public static final String BADGES = "badges.png"; + public static final String AMULET = "amulet.png"; + + public static final String CHROME = "chrome.png"; + public static final String ICONS = "icons.png"; + public static final String STATUS = "status_pane.png"; + public static final String HP_BAR = "hp_bar.png"; + public static final String XP_BAR = "exp_bar.png"; + public static final String TOOLBAR = "toolbar.png"; + + public static final String WARRIOR = "warrior.png"; + public static final String MAGE = "mage.png"; + public static final String ROGUE = "rogue.png"; + public static final String HUNTRESS = "ranger.png"; + public static final String AVATARS = "avatars.png"; + + public static final String SURFACE = "surface.png"; + + public static final String FIREBALL = "fireball.png"; + public static final String SPECKS = "specks.png"; + public static final String EFFECTS = "effects.png"; + + public static final String RAT = "rat.png"; + public static final String GNOLL = "gnoll.png"; + public static final String CRAB = "crab.png"; + public static final String GOO = "goo.png"; + public static final String SWARM = "swarm.png"; + public static final String SKELETON = "skeleton.png"; + public static final String SHAMAN = "shaman.png"; + public static final String THIEF = "thief.png"; + public static final String TENGU = "tengu.png"; + public static final String SHEEP = "sheep.png"; + public static final String KEEPER = "shopkeeper.png"; + public static final String BAT = "bat.png"; + public static final String BRUTE = "brute.png"; + public static final String SPINNER = "spinner.png"; + public static final String DM300 = "dm300.png"; + public static final String WRAITH = "wraith.png"; + public static final String ELEMENTAL= "elemental.png"; + public static final String MONK = "monk.png"; + public static final String WARLOCK = "warlock.png"; + public static final String GOLEM = "golem.png"; + public static final String UNDEAD = "undead.png"; + public static final String KING = "king.png"; + public static final String STATUE = "statue.png"; + public static final String PIRANHA = "piranha.png"; + public static final String EYE = "eye.png"; + public static final String SUCCUBUS = "succubus.png"; + public static final String SCORPIO = "scorpio.png"; + public static final String ROTTING = "rotting_fist.png"; + public static final String BURNING = "burning_fist.png"; + public static final String YOG = "yog.png"; + public static final String LARVA = "larva.png"; + public static final String GHOST = "ghost.png"; + public static final String MAKER = "wandmaker.png"; + public static final String TROLL = "blacksmith.png"; + public static final String IMP = "demon.png"; + public static final String RATKING = "ratking.png"; + + public static final String ITEMS = "items.png"; + public static final String PLANTS = "plants.png"; + + public static final String TILES_SEWERS = "tiles0.png"; + public static final String TILES_PRISON = "tiles1.png"; + public static final String TILES_CAVES = "tiles2.png"; + public static final String TILES_CITY = "tiles3.png"; + public static final String TILES_HALLS = "tiles4.png"; + + public static final String WATER_SEWERS = "water0.png"; + public static final String WATER_PRISON = "water1.png"; + public static final String WATER_CAVES = "water2.png"; + public static final String WATER_CITY = "water3.png"; + public static final String WATER_HALLS = "water4.png"; + + public static final String BUFFS_SMALL = "buffs.png"; + public static final String BUFFS_LARGE = "large_buffs.png"; + public static final String SPELL_ICONS = "spell_icons.png"; + + public static final String FONTS1X = "font1x.png"; + public static final String FONTS15X = "font15x.png"; + public static final String FONTS2X = "font2x.png"; + public static final String FONTS25X = "font25x.png"; + public static final String FONTS3X = "font3x.png"; + + public static final String THEME = "theme.mp3"; + public static final String TUNE = "game.mp3"; + public static final String HAPPY = "surface.mp3"; + + public static final String SND_CLICK = "snd_click.mp3"; + public static final String SND_BADGE = "snd_badge.mp3"; + public static final String SND_GOLD = "snd_gold.mp3"; + + public static final String SND_OPEN = "snd_door_open.mp3"; + public static final String SND_UNLOCK = "snd_unlock.mp3"; + public static final String SND_ITEM = "snd_item.mp3"; + public static final String SND_DEWDROP = "snd_dewdrop.mp3"; + public static final String SND_HIT = "snd_hit.mp3"; + public static final String SND_MISS = "snd_miss.mp3"; + public static final String SND_STEP = "snd_step.mp3"; + public static final String SND_WATER = "snd_water.mp3"; + public static final String SND_DESCEND = "snd_descend.mp3"; + public static final String SND_EAT = "snd_eat.mp3"; + public static final String SND_READ = "snd_read.mp3"; + public static final String SND_LULLABY = "snd_lullaby.mp3"; + public static final String SND_DRINK = "snd_drink.mp3"; + public static final String SND_SHATTER = "snd_shatter.mp3"; + public static final String SND_ZAP = "snd_zap.mp3"; + public static final String SND_LIGHTNING= "snd_lightning.mp3"; + public static final String SND_LEVELUP = "snd_levelup.mp3"; + public static final String SND_DEATH = "snd_death.mp3"; + public static final String SND_CHALLENGE= "snd_challenge.mp3"; + public static final String SND_CURSED = "snd_cursed.mp3"; + public static final String SND_TRAP = "snd_trap.mp3"; + public static final String SND_EVOKE = "snd_evoke.mp3"; + public static final String SND_TOMB = "snd_tomb.mp3"; + public static final String SND_ALERT = "snd_alert.mp3"; + public static final String SND_MELD = "snd_meld.mp3"; + public static final String SND_BOSS = "snd_boss.mp3"; + public static final String SND_BLAST = "snd_blast.mp3"; + public static final String SND_PLANT = "snd_plant.mp3"; + public static final String SND_RAY = "snd_ray.mp3"; + public static final String SND_BEACON = "snd_beacon.mp3"; + public static final String SND_TELEPORT = "snd_teleport.mp3"; + public static final String SND_CHARMS = "snd_charms.mp3"; + public static final String SND_MASTERY = "snd_mastery.mp3"; + public static final String SND_PUFF = "snd_puff.mp3"; + public static final String SND_ROCKS = "snd_rocks.mp3"; + public static final String SND_BURNING = "snd_burning.mp3"; + public static final String SND_FALLING = "snd_falling.mp3"; + public static final String SND_GHOST = "snd_ghost.mp3"; + public static final String SND_SECRET = "snd_secret.mp3"; + public static final String SND_BONES = "snd_bones.mp3"; +} diff --git a/src/com/watabou/pixeldungeon/Badges.java b/src/com/watabou/pixeldungeon/Badges.java new file mode 100644 index 0000000000..1cb3ad9efb --- /dev/null +++ b/src/com/watabou/pixeldungeon/Badges.java @@ -0,0 +1,927 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.actors.mobs.Acidic; +import com.watabou.pixeldungeon.actors.mobs.Albino; +import com.watabou.pixeldungeon.actors.mobs.Bandit; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.actors.mobs.Senior; +import com.watabou.pixeldungeon.actors.mobs.Shielded; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.bags.ScrollHolder; +import com.watabou.pixeldungeon.items.bags.SeedPouch; +import com.watabou.pixeldungeon.items.bags.WandHolster; +import com.watabou.pixeldungeon.items.potions.Potion; +import com.watabou.pixeldungeon.items.rings.Ring; +import com.watabou.pixeldungeon.items.rings.RingOfHaggler; +import com.watabou.pixeldungeon.items.rings.RingOfThorns; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; + +public class Badges { + + public static enum Badge { + MONSTERS_SLAIN_1( "10 enemies slain", 0 ), + MONSTERS_SLAIN_2( "50 enemies slain", 1 ), + MONSTERS_SLAIN_3( "150 enemies slain", 2 ), + MONSTERS_SLAIN_4( "250 enemies slain", 3 ), + GOLD_COLLECTED_1( "100 gold collected", 4 ), + GOLD_COLLECTED_2( "500 gold collected", 5 ), + GOLD_COLLECTED_3( "2500 gold collected", 6 ), + GOLD_COLLECTED_4( "7500 gold collected", 7 ), + LEVEL_REACHED_1( "Level 6 reached", 8 ), + LEVEL_REACHED_2( "Level 12 reached", 9 ), + LEVEL_REACHED_3( "Level 18 reached", 10 ), + LEVEL_REACHED_4( "Level 24 reached", 11 ), + ALL_POTIONS_IDENTIFIED( "All potions identified", 16 ), + ALL_SCROLLS_IDENTIFIED( "All scrolls identified", 17 ), + ALL_RINGS_IDENTIFIED( "All rings identified", 18 ), + ALL_WANDS_IDENTIFIED( "All wands identified", 19 ), + ALL_ITEMS_IDENTIFIED( "All potions, scrolls, rings & wands identified", 35, true ), + BAG_BOUGHT_SEED_POUCH, + BAG_BOUGHT_SCROLL_HOLDER, + BAG_BOUGHT_WAND_HOLSTER, + ALL_BAGS_BOUGHT( "All bags bought", 23 ), + DEATH_FROM_FIRE( "Death from fire", 24 ), + DEATH_FROM_POISON( "Death from poison", 25 ), + DEATH_FROM_GAS( "Death from toxic gas", 26 ), + DEATH_FROM_HUNGER( "Death from hunger", 27 ), + DEATH_FROM_GLYPH( "Death from a glyph", 57 ), + DEATH_FROM_FALLING( "Death from falling down", 59 ), + YASD( "Death from fire, poison, toxic gas & hunger", 34, true ), + BOSS_SLAIN_1_WARRIOR, + BOSS_SLAIN_1_MAGE, + BOSS_SLAIN_1_ROGUE, + BOSS_SLAIN_1_HUNTRESS, + BOSS_SLAIN_1( "1st boss slain", 12 ), + BOSS_SLAIN_2( "2nd boss slain", 13 ), + BOSS_SLAIN_3( "3rd boss slain", 14 ), + BOSS_SLAIN_4( "4th boss slain", 15 ), + BOSS_SLAIN_1_ALL_CLASSES( "1st boss slain by Warrior, Mage, Rogue & Huntress", 32, true ), + BOSS_SLAIN_3_GLADIATOR, + BOSS_SLAIN_3_BERSERKER, + BOSS_SLAIN_3_WARLOCK, + BOSS_SLAIN_3_BATTLEMAGE, + BOSS_SLAIN_3_FREERUNNER, + BOSS_SLAIN_3_ASSASSIN, + BOSS_SLAIN_3_SNIPER, + BOSS_SLAIN_3_WARDEN, + BOSS_SLAIN_3_ALL_SUBCLASSES( + "3rd boss slain by Gladiator, Berserker, Warlock, Battlemage, " + + "Freerunner, Assassin, Sniper & Warden", 33, true ), + RING_OF_HAGGLER( "Ring of Haggler obtained", 20 ), + RING_OF_THORNS( "Ring of Thorns obtained", 21 ), + STRENGTH_ATTAINED_1( "13 points of Strength attained", 40 ), + STRENGTH_ATTAINED_2( "15 points of Strength attained", 41 ), + STRENGTH_ATTAINED_3( "17 points of Strength attained", 42 ), + STRENGTH_ATTAINED_4( "19 points of Strength attained", 43 ), + FOOD_EATEN_1( "10 pieces of food eaten", 44 ), + FOOD_EATEN_2( "20 pieces of food eaten", 45 ), + FOOD_EATEN_3( "30 pieces of food eaten", 46 ), + FOOD_EATEN_4( "40 pieces of food eaten", 47 ), + MASTERY_WARRIOR, + MASTERY_MAGE, + MASTERY_ROGUE, + MASTERY_HUNTRESS, + ITEM_LEVEL_1( "Item of level 3 acquired", 48 ), + ITEM_LEVEL_2( "Item of level 6 acquired", 49 ), + ITEM_LEVEL_3( "Item of level 9 acquired", 50 ), + ITEM_LEVEL_4( "Item of level 12 acquired", 51 ), + RARE_ALBINO, + RARE_BANDIT, + RARE_SHIELDED, + RARE_SENIOR, + RARE_ACIDIC, + RARE( "All rare monsters slain", 37, true ), + VICTORY_WARRIOR, + VICTORY_MAGE, + VICTORY_ROGUE, + VICTORY_HUNTRESS, + VICTORY( "Amulet of Yendor obtained", 22 ), + VICTORY_ALL_CLASSES( "Amulet of Yendor obtained by Warrior, Mage, Rogue & Huntress", 36, true ), + MASTERY_COMBO( "7-hit combo", 56 ), + POTIONS_COOKED_1( "3 potions cooked", 52 ), + POTIONS_COOKED_2( "6 potions cooked", 53 ), + POTIONS_COOKED_3( "9 potions cooked", 54 ), + POTIONS_COOKED_4( "12 potions cooked", 55 ), + NO_MONSTERS_SLAIN( "Level completed without killing any monsters", 28 ), + GRIM_WEAPON( "Monster killed by a Grim weapon", 29 ), + PIRANHAS( "6 piranhas killed", 30 ), + NIGHT_HUNTER( "15 monsters killed at nighttime", 58 ), + GAMES_PLAYED_1( "10 games played", 60, true ), + GAMES_PLAYED_2( "100 games played", 61, true ), + GAMES_PLAYED_3( "500 games played", 62, true ), + GAMES_PLAYED_4( "2000 games played", 63, true ), + HAPPY_END( "Happy end", 38 ), + SUPPORTER( "Thanks for your support!", 31, true ); + + public boolean meta; + + public String description; + public int image; + + private Badge( String description, int image ) { + this( description, image, false ); + } + + private Badge( String description, int image, boolean meta ) { + this.description = description; + this.image = image; + this.meta = meta; + } + + private Badge() { + this( "", -1 ); + } + } + + private static HashSet global; + private static HashSet local = new HashSet(); + + private static boolean saveNeeded = false; + + public static void reset() { + local.clear(); + loadGlobal(); + } + + private static final String BADGES_FILE = "badges.dat"; + private static final String BADGES = "badges"; + + private static HashSet restore( Bundle bundle ) { + HashSet badges = new HashSet(); + + String[] names = bundle.getStringArray( BADGES ); + for (int i=0; i < names.length; i++) { + try { + badges.add( Badge.valueOf( names[i] ) ); + } catch (Exception e) { + } + } + + return badges; + } + + private static void store( Bundle bundle, HashSet badges ) { + int count = 0; + String names[] = new String[global.size()]; + + for (Badge badge:badges) { + names[count++] = badge.toString(); + } + bundle.put( BADGES, names ); + } + + public static void loadLocal( Bundle bundle ) { + local = restore( bundle ); + } + + public static void saveLocal( Bundle bundle ) { + store( bundle, local ); + } + + public static void loadGlobal() { + if (global == null) { + try { + InputStream input = Game.instance.openFileInput( BADGES_FILE ); + Bundle bundle = Bundle.read( input ); + input.close(); + + global = restore( bundle ); + + } catch (IOException e) { + global = new HashSet(); + } + } + } + + public static void saveGlobal() { + if (saveNeeded) { + + Bundle bundle = new Bundle(); + store( bundle, global ); + + try { + OutputStream output = Game.instance.openFileOutput( BADGES_FILE, Game.MODE_PRIVATE ); + Bundle.write( bundle, output ); + output.close(); + saveNeeded = false; + } catch (IOException e) { + + } + } + } + + public static void validateMonstersSlain() { + Badge badge = null; + + if (!local.contains( Badge.MONSTERS_SLAIN_1 ) && Statistics.enemiesSlain >= 10) { + badge = Badge.MONSTERS_SLAIN_1; + local.add( badge ); + } + if (!local.contains( Badge.MONSTERS_SLAIN_2 ) && Statistics.enemiesSlain >= 50) { + badge = Badge.MONSTERS_SLAIN_2; + local.add( badge ); + } + if (!local.contains( Badge.MONSTERS_SLAIN_3 ) && Statistics.enemiesSlain >= 150) { + badge = Badge.MONSTERS_SLAIN_3; + local.add( badge ); + } + if (!local.contains( Badge.MONSTERS_SLAIN_4 ) && Statistics.enemiesSlain >= 250) { + badge = Badge.MONSTERS_SLAIN_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validateGoldCollected() { + Badge badge = null; + + if (!local.contains( Badge.GOLD_COLLECTED_1 ) && Statistics.goldCollected >= 100) { + badge = Badge.GOLD_COLLECTED_1; + local.add( badge ); + } + if (!local.contains( Badge.GOLD_COLLECTED_2 ) && Statistics.goldCollected >= 500) { + badge = Badge.GOLD_COLLECTED_2; + local.add( badge ); + } + if (!local.contains( Badge.GOLD_COLLECTED_3 ) && Statistics.goldCollected >= 2500) { + badge = Badge.GOLD_COLLECTED_3; + local.add( badge ); + } + if (!local.contains( Badge.GOLD_COLLECTED_4 ) && Statistics.goldCollected >= 7500) { + badge = Badge.GOLD_COLLECTED_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validateLevelReached() { + Badge badge = null; + + if (!local.contains( Badge.LEVEL_REACHED_1 ) && Dungeon.hero.lvl >= 6) { + badge = Badge.LEVEL_REACHED_1; + local.add( badge ); + } + if (!local.contains( Badge.LEVEL_REACHED_2 ) && Dungeon.hero.lvl >= 12) { + badge = Badge.LEVEL_REACHED_2; + local.add( badge ); + } + if (!local.contains( Badge.LEVEL_REACHED_3 ) && Dungeon.hero.lvl >= 18) { + badge = Badge.LEVEL_REACHED_3; + local.add( badge ); + } + if (!local.contains( Badge.LEVEL_REACHED_4 ) && Dungeon.hero.lvl >= 24) { + badge = Badge.LEVEL_REACHED_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validateStrengthAttained() { + Badge badge = null; + + if (!local.contains( Badge.STRENGTH_ATTAINED_1 ) && Dungeon.hero.STR >= 13) { + badge = Badge.STRENGTH_ATTAINED_1; + local.add( badge ); + } + if (!local.contains( Badge.STRENGTH_ATTAINED_2 ) && Dungeon.hero.STR >= 15) { + badge = Badge.STRENGTH_ATTAINED_2; + local.add( badge ); + } + if (!local.contains( Badge.STRENGTH_ATTAINED_3 ) && Dungeon.hero.STR >= 17) { + badge = Badge.STRENGTH_ATTAINED_3; + local.add( badge ); + } + if (!local.contains( Badge.STRENGTH_ATTAINED_4 ) && Dungeon.hero.STR >= 19) { + badge = Badge.STRENGTH_ATTAINED_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validateFoodEaten() { + Badge badge = null; + + if (!local.contains( Badge.FOOD_EATEN_1 ) && Statistics.foodEaten >= 10) { + badge = Badge.FOOD_EATEN_1; + local.add( badge ); + } + if (!local.contains( Badge.FOOD_EATEN_2 ) && Statistics.foodEaten >= 20) { + badge = Badge.FOOD_EATEN_2; + local.add( badge ); + } + if (!local.contains( Badge.FOOD_EATEN_3 ) && Statistics.foodEaten >= 30) { + badge = Badge.FOOD_EATEN_3; + local.add( badge ); + } + if (!local.contains( Badge.FOOD_EATEN_4 ) && Statistics.foodEaten >= 40) { + badge = Badge.FOOD_EATEN_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validatePotionsCooked() { + Badge badge = null; + + if (!local.contains( Badge.POTIONS_COOKED_1 ) && Statistics.potionsCooked >= 3) { + badge = Badge.POTIONS_COOKED_1; + local.add( badge ); + } + if (!local.contains( Badge.POTIONS_COOKED_2 ) && Statistics.potionsCooked >= 6) { + badge = Badge.POTIONS_COOKED_2; + local.add( badge ); + } + if (!local.contains( Badge.POTIONS_COOKED_3 ) && Statistics.potionsCooked >= 9) { + badge = Badge.POTIONS_COOKED_3; + local.add( badge ); + } + if (!local.contains( Badge.POTIONS_COOKED_4 ) && Statistics.potionsCooked >= 12) { + badge = Badge.POTIONS_COOKED_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validatePiranhasKilled() { + Badge badge = null; + + if (!local.contains( Badge.PIRANHAS ) && Statistics.piranhasKilled >= 6) { + badge = Badge.PIRANHAS; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validateItemLevelAquired( Item item ) { + + // This method should be called: + // 1) When an item is obtained (Item.collect) + // 2) When an item is upgraded (ScrollOfUpgrade, ScrollOfWeaponUpgrade, ShortSword, WandOfMagicMissile) + // 3) When an item is identified + if (!item.levelKnown) { + return; + } + + Badge badge = null; + + if (!local.contains( Badge.ITEM_LEVEL_1 ) && item.level >= 3) { + badge = Badge.ITEM_LEVEL_1; + local.add( badge ); + } + if (!local.contains( Badge.ITEM_LEVEL_2 ) && item.level >= 6) { + badge = Badge.ITEM_LEVEL_2; + local.add( badge ); + } + if (!local.contains( Badge.ITEM_LEVEL_3 ) && item.level >= 9) { + badge = Badge.ITEM_LEVEL_3; + local.add( badge ); + } + if (!local.contains( Badge.ITEM_LEVEL_4 ) && item.level >= 12) { + badge = Badge.ITEM_LEVEL_4; + local.add( badge ); + } + + displayBadge( badge ); + } + + public static void validateAllPotionsIdentified() { + if (Dungeon.hero != null && Dungeon.hero.isAlive() && + !local.contains( Badge.ALL_POTIONS_IDENTIFIED ) && Potion.allKnown()) { + + Badge badge = Badge.ALL_POTIONS_IDENTIFIED; + local.add( badge ); + displayBadge( badge ); + + validateAllItemsIdentified(); + } + } + + public static void validateAllScrollsIdentified() { + if (Dungeon.hero != null && Dungeon.hero.isAlive() && + !local.contains( Badge.ALL_SCROLLS_IDENTIFIED ) && Scroll.allKnown()) { + + Badge badge = Badge.ALL_SCROLLS_IDENTIFIED; + local.add( badge ); + displayBadge( badge ); + + validateAllItemsIdentified(); + } + } + + public static void validateAllRingsIdentified() { + if (Dungeon.hero != null && Dungeon.hero.isAlive() && + !local.contains( Badge.ALL_RINGS_IDENTIFIED ) && Ring.allKnown()) { + + Badge badge = Badge.ALL_RINGS_IDENTIFIED; + local.add( badge ); + displayBadge( badge ); + + validateAllItemsIdentified(); + } + } + + public static void validateAllWandsIdentified() { + if (Dungeon.hero != null && Dungeon.hero.isAlive() && + !local.contains( Badge.ALL_WANDS_IDENTIFIED ) && Wand.allKnown()) { + + Badge badge = Badge.ALL_WANDS_IDENTIFIED; + local.add( badge ); + displayBadge( badge ); + + validateAllItemsIdentified(); + } + } + + public static void validateAllBagsBought( Item bag ) { + + Badge badge = null; + if (bag instanceof SeedPouch) { + badge = Badge.BAG_BOUGHT_SEED_POUCH; + } else if (bag instanceof ScrollHolder) { + badge = Badge.BAG_BOUGHT_SCROLL_HOLDER; + } else if (bag instanceof WandHolster) { + badge = Badge.BAG_BOUGHT_WAND_HOLSTER; + } + + if (badge != null) { + + local.add( badge ); + + if (!local.contains( Badge.ALL_BAGS_BOUGHT ) && + local.contains( Badge.BAG_BOUGHT_SCROLL_HOLDER ) && + local.contains( Badge.BAG_BOUGHT_SEED_POUCH ) && + local.contains( Badge.BAG_BOUGHT_WAND_HOLSTER )) { + + badge = Badge.ALL_BAGS_BOUGHT; + local.add( badge ); + displayBadge( badge ); + } + } + } + + public static void validateAllItemsIdentified() { + if (!global.contains( Badge.ALL_ITEMS_IDENTIFIED ) && + global.contains( Badge.ALL_POTIONS_IDENTIFIED ) && + global.contains( Badge.ALL_SCROLLS_IDENTIFIED ) && + global.contains( Badge.ALL_RINGS_IDENTIFIED ) && + global.contains( Badge.ALL_WANDS_IDENTIFIED )) { + + Badge badge = Badge.ALL_ITEMS_IDENTIFIED; + displayBadge( badge ); + } + } + + public static void validateDeathFromFire() { + Badge badge = Badge.DEATH_FROM_FIRE; + local.add( badge ); + displayBadge( badge ); + + validateYASD(); + } + + public static void validateDeathFromPoison() { + Badge badge = Badge.DEATH_FROM_POISON; + local.add( badge ); + displayBadge( badge ); + + validateYASD(); + } + + public static void validateDeathFromGas() { + Badge badge = Badge.DEATH_FROM_GAS; + local.add( badge ); + displayBadge( badge ); + + validateYASD(); + } + + public static void validateDeathFromHunger() { + Badge badge = Badge.DEATH_FROM_HUNGER; + local.add( badge ); + displayBadge( badge ); + + validateYASD(); + } + + public static void validateDeathFromGlyph() { + Badge badge = Badge.DEATH_FROM_GLYPH; + local.add( badge ); + displayBadge( badge ); + } + + public static void validateDeathFromFalling() { + Badge badge = Badge.DEATH_FROM_FALLING; + local.add( badge ); + displayBadge( badge ); + } + + private static void validateYASD() { + if (global.contains( Badge.DEATH_FROM_FIRE ) && + global.contains( Badge.DEATH_FROM_POISON ) && + global.contains( Badge.DEATH_FROM_GAS ) && + global.contains( Badge.DEATH_FROM_HUNGER)) { + + Badge badge = Badge.YASD; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateBossSlain() { + Badge badge = null; + switch (Dungeon.depth) { + case 5: + badge = Badge.BOSS_SLAIN_1; + break; + case 10: + badge = Badge.BOSS_SLAIN_2; + break; + case 15: + badge = Badge.BOSS_SLAIN_3; + break; + case 20: + badge = Badge.BOSS_SLAIN_4; + break; + } + + if (badge != null) { + local.add( badge ); + displayBadge( badge ); + + if (badge == Badge.BOSS_SLAIN_1) { + switch (Dungeon.hero.heroClass) { + case WARRIOR: + badge = Badge.BOSS_SLAIN_1_WARRIOR; + break; + case MAGE: + badge = Badge.BOSS_SLAIN_1_MAGE; + break; + case ROGUE: + badge = Badge.BOSS_SLAIN_1_ROGUE; + break; + case HUNTRESS: + badge = Badge.BOSS_SLAIN_1_HUNTRESS; + break; + } + local.add( badge ); + if (!global.contains( badge )) { + global.add( badge ); + saveNeeded = true; + } + + if (global.contains( Badge.BOSS_SLAIN_1_WARRIOR ) && + global.contains( Badge.BOSS_SLAIN_1_MAGE ) && + global.contains( Badge.BOSS_SLAIN_1_ROGUE ) && + global.contains( Badge.BOSS_SLAIN_1_HUNTRESS)) { + + badge = Badge.BOSS_SLAIN_1_ALL_CLASSES; + if (!global.contains( badge )) { + displayBadge( badge ); + global.add( badge ); + saveNeeded = true; + } + } + } else + if (badge == Badge.BOSS_SLAIN_3) { + switch (Dungeon.hero.subClass) { + case GLADIATOR: + badge = Badge.BOSS_SLAIN_3_GLADIATOR; + break; + case BERSERKER: + badge = Badge.BOSS_SLAIN_3_BERSERKER; + break; + case WARLOCK: + badge = Badge.BOSS_SLAIN_3_WARLOCK; + break; + case BATTLEMAGE: + badge = Badge.BOSS_SLAIN_3_BATTLEMAGE; + break; + case FREERUNNER: + badge = Badge.BOSS_SLAIN_3_FREERUNNER; + break; + case ASSASSIN: + badge = Badge.BOSS_SLAIN_3_ASSASSIN; + break; + case SNIPER: + badge = Badge.BOSS_SLAIN_3_SNIPER; + break; + case WARDEN: + badge = Badge.BOSS_SLAIN_3_WARDEN; + break; + default: + return; + } + local.add( badge ); + if (!global.contains( badge )) { + global.add( badge ); + saveNeeded = true; + } + + if (global.contains( Badge.BOSS_SLAIN_3_GLADIATOR ) && + global.contains( Badge.BOSS_SLAIN_3_BERSERKER ) && + global.contains( Badge.BOSS_SLAIN_3_WARLOCK ) && + global.contains( Badge.BOSS_SLAIN_3_BATTLEMAGE ) && + global.contains( Badge.BOSS_SLAIN_3_FREERUNNER ) && + global.contains( Badge.BOSS_SLAIN_3_ASSASSIN ) && + global.contains( Badge.BOSS_SLAIN_3_SNIPER ) && + global.contains( Badge.BOSS_SLAIN_3_WARDEN )) { + + badge = Badge.BOSS_SLAIN_3_ALL_SUBCLASSES; + if (!global.contains( badge )) { + displayBadge( badge ); + global.add( badge ); + saveNeeded = true; + } + } + } + } + } + + public static void validateMastery() { + + Badge badge = null; + switch (Dungeon.hero.heroClass) { + case WARRIOR: + badge = Badge.MASTERY_WARRIOR; + break; + case MAGE: + badge = Badge.MASTERY_MAGE; + break; + case ROGUE: + badge = Badge.MASTERY_ROGUE; + break; + case HUNTRESS: + badge = Badge.MASTERY_HUNTRESS; + break; + } + + if (!global.contains( badge )) { + global.add( badge ); + saveNeeded = true; + } + } + + public static void validateMasteryCombo( int n ) { + if (!local.contains( Badge.MASTERY_COMBO ) && n == 7) { + Badge badge = Badge.MASTERY_COMBO; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateRingOfHaggler() { + if (!local.contains( Badge.RING_OF_HAGGLER ) && new RingOfHaggler().isKnown()) { + Badge badge = Badge.RING_OF_HAGGLER; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateRingOfThorns() { + if (!local.contains( Badge.RING_OF_THORNS ) && new RingOfThorns().isKnown()) { + Badge badge = Badge.RING_OF_THORNS; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateRare( Mob mob ) { + + Badge badge = null; + if (mob instanceof Albino) { + badge = Badge.RARE_ALBINO; + } else if (mob instanceof Bandit) { + badge = Badge.RARE_BANDIT; + } else if (mob instanceof Shielded) { + badge = Badge.RARE_SHIELDED; + } else if (mob instanceof Senior) { + badge = Badge.RARE_SENIOR; + } else if (mob instanceof Acidic) { + badge = Badge.RARE_ACIDIC; + } + if (!global.contains( badge )) { + global.add( badge ); + saveNeeded = true; + } + + if (global.contains( Badge.RARE_ALBINO ) && + global.contains( Badge.RARE_BANDIT ) && + global.contains( Badge.RARE_SHIELDED ) && + global.contains( Badge.RARE_SENIOR ) && + global.contains( Badge.RARE_ACIDIC )) { + + badge = Badge.RARE; + displayBadge( badge ); + } + } + + public static void validateVictory() { + + Badge badge = Badge.VICTORY; + displayBadge( badge ); + + switch (Dungeon.hero.heroClass) { + case WARRIOR: + badge = Badge.VICTORY_WARRIOR; + break; + case MAGE: + badge = Badge.VICTORY_MAGE; + break; + case ROGUE: + badge = Badge.VICTORY_ROGUE; + break; + case HUNTRESS: + badge = Badge.VICTORY_HUNTRESS; + break; + } + local.add( badge ); + if (!global.contains( badge )) { + global.add( badge ); + saveNeeded = true; + } + + if (global.contains( Badge.VICTORY_WARRIOR ) && + global.contains( Badge.VICTORY_MAGE ) && + global.contains( Badge.VICTORY_ROGUE ) && + global.contains( Badge.VICTORY_HUNTRESS )) { + + badge = Badge.VICTORY_ALL_CLASSES; + displayBadge( badge ); + } + } + + public static void validateNoKilling() { + if (!local.contains( Badge.NO_MONSTERS_SLAIN ) && Statistics.completedWithNoKilling) { + Badge badge = Badge.NO_MONSTERS_SLAIN; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateGrimWeapon() { + if (!local.contains( Badge.GRIM_WEAPON )) { + Badge badge = Badge.GRIM_WEAPON; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateNightHunter() { + if (!local.contains( Badge.NIGHT_HUNTER ) && Statistics.nightHunt >= 15) { + Badge badge = Badge.NIGHT_HUNTER; + local.add( badge ); + displayBadge( badge ); + } + } + + public static void validateSupporter() { + + global.add( Badge.SUPPORTER ); + saveNeeded = true; + + PixelScene.showBadge( Badge.SUPPORTER ); + } + + public static void validateGamesPlayed() { + Badge badge = null; + if (Rankings.INSTANCE.totalNumber >= 10) { + badge = Badge.GAMES_PLAYED_1; + } + if (Rankings.INSTANCE.totalNumber >= 100) { + badge = Badge.GAMES_PLAYED_2; + } + if (Rankings.INSTANCE.totalNumber >= 500) { + badge = Badge.GAMES_PLAYED_3; + } + if (Rankings.INSTANCE.totalNumber >= 2000) { + badge = Badge.GAMES_PLAYED_4; + } + + displayBadge( badge ); + } + + public static void validateHappyEnd() { + displayBadge( Badge.HAPPY_END ); + } + + private static void displayBadge( Badge badge ) { + + if (badge == null) { + return; + } + + if (global.contains( badge )) { + + if (!badge.meta) { + GLog.h( "Badge endorsed: %s", badge.description ); + } + + } else { + + global.add( badge ); + saveNeeded = true; + + if (badge.meta) { + GLog.h( "New super badge: %s", badge.description ); + } else { + GLog.h( "New badge: %s", badge.description ); + } + PixelScene.showBadge( badge ); + } + } + + public static boolean isUnlocked( Badge badge ) { + return global.contains( badge ); + } + + public static void disown( Badge badge ) { + loadGlobal(); + global.remove( badge ); + saveNeeded = true; + } + + public static List filtered( boolean global ) { + + HashSet filtered = new HashSet( global ? Badges.global : Badges.local ); + + if (!global) { + Iterator iterator = filtered.iterator(); + while (iterator.hasNext()) { + Badge badge = iterator.next(); + if (badge.meta) { + iterator.remove(); + } + } + } + + leaveBest( filtered, Badge.MONSTERS_SLAIN_1, Badge.MONSTERS_SLAIN_2, Badge.MONSTERS_SLAIN_3, Badge.MONSTERS_SLAIN_4 ); + leaveBest( filtered, Badge.GOLD_COLLECTED_1, Badge.GOLD_COLLECTED_2, Badge.GOLD_COLLECTED_3, Badge.GOLD_COLLECTED_4 ); + leaveBest( filtered, Badge.BOSS_SLAIN_1, Badge.BOSS_SLAIN_2, Badge.BOSS_SLAIN_3, Badge.BOSS_SLAIN_4 ); + leaveBest( filtered, Badge.LEVEL_REACHED_1, Badge.LEVEL_REACHED_2, Badge.LEVEL_REACHED_3, Badge.LEVEL_REACHED_4 ); + leaveBest( filtered, Badge.STRENGTH_ATTAINED_1, Badge.STRENGTH_ATTAINED_2, Badge.STRENGTH_ATTAINED_3, Badge.STRENGTH_ATTAINED_4 ); + leaveBest( filtered, Badge.FOOD_EATEN_1, Badge.FOOD_EATEN_2, Badge.FOOD_EATEN_3, Badge.FOOD_EATEN_4 ); + leaveBest( filtered, Badge.ITEM_LEVEL_1, Badge.ITEM_LEVEL_2, Badge.ITEM_LEVEL_3, Badge.ITEM_LEVEL_4 ); + leaveBest( filtered, Badge.POTIONS_COOKED_1, Badge.POTIONS_COOKED_2, Badge.POTIONS_COOKED_3, Badge.POTIONS_COOKED_4 ); + leaveBest( filtered, Badge.BOSS_SLAIN_1_ALL_CLASSES, Badge.BOSS_SLAIN_3_ALL_SUBCLASSES ); + leaveBest( filtered, Badge.DEATH_FROM_FIRE, Badge.YASD ); + leaveBest( filtered, Badge.DEATH_FROM_GAS, Badge.YASD ); + leaveBest( filtered, Badge.DEATH_FROM_HUNGER, Badge.YASD ); + leaveBest( filtered, Badge.DEATH_FROM_POISON, Badge.YASD ); + leaveBest( filtered, Badge.VICTORY, Badge.VICTORY_ALL_CLASSES ); + leaveBest( filtered, Badge.GAMES_PLAYED_1, Badge.GAMES_PLAYED_2, Badge.GAMES_PLAYED_3, Badge.GAMES_PLAYED_4 ); + + ArrayList list = new ArrayList( filtered ); + Collections.sort( list ); + + return list; + } + + private static void leaveBest( HashSet list, Badge...badges ) { + for (int i=badges.length-1; i > 0; i--) { + if (list.contains( badges[i])) { + for (int j=0; j < i; j++) { + list.remove( badges[j] ); + } + break; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/Bones.java b/src/com/watabou/pixeldungeon/Bones.java new file mode 100644 index 0000000000..4114784950 --- /dev/null +++ b/src/com/watabou/pixeldungeon/Bones.java @@ -0,0 +1,125 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.rings.Ring; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Bones { + + private static final String BONES_FILE = "bones.dat"; + + private static final String LEVEL = "level"; + private static final String ITEM = "item"; + + private static int depth = -1; + private static Item item; + + public static void leave() { + + item = null; + switch (Random.Int( 4 )) { + case 0: + item = Dungeon.hero.belongings.weapon; + break; + case 1: + item = Dungeon.hero.belongings.armor; + break; + case 2: + item = Dungeon.hero.belongings.ring1; + break; + case 3: + item = Dungeon.hero.belongings.ring2; + break; + } + if (item == null) { + if (Dungeon.gold > 0) { + item = new Gold( Random.IntRange( 1, Dungeon.gold ) ); + } else { + item = new Gold( 1 ); + } + } + + depth = Dungeon.depth; + + Bundle bundle = new Bundle(); + bundle.put( LEVEL, depth ); + bundle.put( ITEM, item ); + + try { + OutputStream output = Game.instance.openFileOutput( BONES_FILE, Game.MODE_PRIVATE ); + Bundle.write( bundle, output ); + output.close(); + } catch (IOException e) { + + } + } + + public static Item get() { + if (depth == -1) { + + try { + InputStream input = Game.instance.openFileInput( BONES_FILE ) ; + Bundle bundle = Bundle.read( input ); + input.close(); + + depth = bundle.getInt( LEVEL ); + item = (Item)bundle.get( ITEM ); + + return get(); + + } catch (IOException e) { + return null; + } + + } else { + if (depth == Dungeon.depth) { + Game.instance.deleteFile( BONES_FILE ); + depth = 0; + + if (!item.stackable) { + item.cursed = true; + item.cursedKnown = true; + if (item.isUpgradable()) { + int lvl = (Dungeon.depth - 1) * 3 / 5 + 1; + if (lvl < item.level) { + item.degrade( item.level - lvl ); + } + item.levelKnown = false; + } + } + + if (item instanceof Ring) { + ((Ring)item).syncGem(); + } + + return item; + } else { + return null; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/Chrome.java b/src/com/watabou/pixeldungeon/Chrome.java new file mode 100644 index 0000000000..ef6d5d93ff --- /dev/null +++ b/src/com/watabou/pixeldungeon/Chrome.java @@ -0,0 +1,66 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import com.watabou.noosa.NinePatch; + +public class Chrome { + + public enum Type { + TOAST, + TOAST_TR, + WINDOW, + BUTTON, + TAG, + GEM, + SCROLL, + TAB_SET, + TAB_SELECTED, + TAB_UNSELECTED, + SURFACE + }; + + public static NinePatch get( Type type ) { + switch (type) { + case WINDOW: + return new NinePatch( Assets.CHROME, 0, 0, 22, 22, 7 ); + case TOAST: + return new NinePatch( Assets.CHROME, 22, 0, 18, 18, 5 ); + case TOAST_TR: + return new NinePatch( Assets.CHROME, 40, 0, 18, 18, 5 ); + case BUTTON: + return new NinePatch( Assets.CHROME, 58, 0, 4, 4, 1 ); + case TAG: + return new NinePatch( Assets.CHROME, 22, 18, 16, 14, 3 ); + case GEM: + return new NinePatch( Assets.CHROME, 0, 32, 32, 32, 13 ); + case SCROLL: + return new NinePatch( Assets.CHROME, 32, 32, 32, 32, 5, 11, 5, 11 ); + case TAB_SET: + return new NinePatch( Assets.CHROME, 64, 0, 22, 22, 7, 7, 7, 7 ); + case TAB_SELECTED: + return new NinePatch( Assets.CHROME, 64, 22, 10, 14, 4, 7, 4, 6 ); + case TAB_UNSELECTED: + return new NinePatch( Assets.CHROME, 74, 22, 10, 14, 4, 7, 4, 6 ); + case SURFACE: + return new NinePatch( Assets.CHROME, 86, 0, 22, 22, 5 ); + default: + return null; + } + } +} diff --git a/src/com/watabou/pixeldungeon/Dungeon.java b/src/com/watabou/pixeldungeon/Dungeon.java new file mode 100644 index 0000000000..f062208117 --- /dev/null +++ b/src/com/watabou/pixeldungeon/Dungeon.java @@ -0,0 +1,690 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.mobs.npcs.Blacksmith; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.actors.mobs.npcs.Ghost; +import com.watabou.pixeldungeon.actors.mobs.npcs.Wandmaker; +import com.watabou.pixeldungeon.items.Ankh; +import com.watabou.pixeldungeon.items.potions.Potion; +import com.watabou.pixeldungeon.items.rings.Ring; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.levels.CavesBossLevel; +import com.watabou.pixeldungeon.levels.CavesLevel; +import com.watabou.pixeldungeon.levels.CityBossLevel; +import com.watabou.pixeldungeon.levels.CityLevel; +import com.watabou.pixeldungeon.levels.DeadEndLevel; +import com.watabou.pixeldungeon.levels.HallsBossLevel; +import com.watabou.pixeldungeon.levels.HallsLevel; +import com.watabou.pixeldungeon.levels.LastLevel; +import com.watabou.pixeldungeon.levels.LastShopLevel; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.PrisonBossLevel; +import com.watabou.pixeldungeon.levels.PrisonLevel; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.SewerBossLevel; +import com.watabou.pixeldungeon.levels.SewerLevel; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.StartScene; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndResurrect; +import com.watabou.utils.Bundle; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; + +public class Dungeon { + + private static final String NO_TIPS = "The text is indecipherable..."; + private static final String[] TIPS = { + "Don't overestimate your strength, use weapons and armor you can handle.", + "Not all doors in the dungeon are visible at first sight. If you are stuck, search for hidden doors.", + "Remember, that raising your strength is not the only way to access better equipment, you can go " + + "the other way lowering its strength requirement with Scrolls of Upgrade.", + "You can spend your gold in shops on deeper levels of the dungeon. The first one is on the 6th level.", + + "Beware of Goo!", + + "Pixel-Mart - all you need for successful adventure!", + "Identify your potions and scrolls as soon as possible. Don't put it off to the moment " + + "when you actually need them.", + "Being hungry doesn't hurt, but starving does hurt.", + "Surprise attack has a better chance to hit. For example, you can ambush your enemy behind " + + "a closed door when you know it is approaching.", + + "Don't let The Tengu out!", + + "Pixel-Mart. Spend money. Live longer.", + "When you're attacked by several monsters at the same time, try to retreat behind a door.", + "If you are burning, you can't put out the fire in the water while levitating.", + "There is no sense in possessing more than one Ankh at the same time, because you will lose them upon resurrecting.", + + "DANGER! Heavy machinery can cause injury, loss of limbs or death!", + + "Pixel-Mart. A safer life in dungeon.", + "When you upgrade an enchanted weapon, there is a chance to destroy that enchantment.", + "In a Well of Transmutation you can get an item, that cannot be obtained otherwise.", + "The only way to enchant a weapon is by upgrading it with a Scroll of Weapon Upgrade.", + + "No weapons allowed in the presence of His Majesty!", + + "Pixel-Mart. Special prices for demon hunters!", + "The text is written in demonic language.", + "The text is written in demonic language.", + "The text is written in demonic language." + }; + + private static final String TXT_DEAD_END = + "What are you doing here?!"; + + public static int potionOfStrength; + public static int scrollsOfUpgrade; + public static int arcaneStyli; + public static boolean dewVial; // true if the dew vial can be spawned + public static int transmutation; // depth number for a well of transmutation + + + public static Hero hero; + public static Level level; + + // Eitherî Item or Class + public static Object quickslot; + + public static int depth; + public static int gold; + // Reason of death + public static String resultDescription; + + public static HashSet chapters; + + // Hero's field of view + public static boolean[] visible = new boolean[Level.LENGTH]; + + public static boolean nightMode; + + public static void init() { + + Actor.clear(); + + PathFinder.setMapSize( Level.WIDTH, Level.HEIGHT ); + + Scroll.initLabels(); + Potion.initColors(); + Wand.initWoods(); + Ring.initGems(); + + Statistics.reset(); + Journal.reset(); + + depth = 0; + gold = 0; + + potionOfStrength = 0; + scrollsOfUpgrade = 0; + arcaneStyli = 0; + dewVial = true; + transmutation = Random.IntRange( 6, 14 ); + + chapters = new HashSet(); + + Ghost.Quest.reset(); + Wandmaker.Quest.reset(); + Blacksmith.Quest.reset(); + Imp.Quest.reset(); + + Room.shuffleTypes(); + + hero = new Hero(); + hero.live(); + + Badges.reset(); + + StartScene.curClass.initHero( hero ); + } + + public static Level newLevel() { + + Dungeon.level = null; + Actor.clear(); + + depth++; + if (depth > Statistics.deepestFloor) { + Statistics.deepestFloor = depth; + + if (Statistics.qualifiedForNoKilling) { + Statistics.completedWithNoKilling = true; + } else { + Statistics.completedWithNoKilling = false; + } + } + + Arrays.fill( visible, false ); + + Level level; + switch (depth) { + case 1: + case 2: + case 3: + case 4: + level = new SewerLevel(); + break; + case 5: + level = new SewerBossLevel(); + break; + case 6: + case 7: + case 8: + case 9: + level = new PrisonLevel(); + break; + case 10: + level = new PrisonBossLevel(); + break; + case 11: + case 12: + case 13: + case 14: + level = new CavesLevel(); + break; + case 15: + level = new CavesBossLevel(); + break; + case 16: + case 17: + case 18: + case 19: + level = new CityLevel(); + break; + case 20: + level = new CityBossLevel(); + break; + case 21: + level = new LastShopLevel(); + break; + case 22: + case 23: + case 24: + level = new HallsLevel(); + break; + case 25: + level = new HallsBossLevel(); + break; + case 26: + level = new LastLevel(); + break; + default: + level = new DeadEndLevel(); + Statistics.deepestFloor--; + } + + level.create(); + + Statistics.qualifiedForNoKilling = !bossLevel(); + + return level; + } + + public static void resetLevel() { + + Actor.clear(); + + Arrays.fill( visible, false ); + + level.reset(); + switchLevel( level, level.entrance ); + } + + public static String tip() { + + if (level instanceof DeadEndLevel) { + + return TXT_DEAD_END; + + } else { + + int index = depth - 1; + + if (index < TIPS.length) { + return TIPS[index]; + } else { + return NO_TIPS; + } + } + } + + public static boolean shopOnLevel() { + return depth == 6 || depth == 11 || depth == 16; + } + + public static boolean bossLevel() { + return bossLevel( depth ); + } + + public static boolean bossLevel( int depth ) { + return depth == 5 || depth == 10 || depth == 15 || depth == 20 || depth == 25; + } + + @SuppressWarnings("deprecation") + public static void switchLevel( final Level level, int pos ) { + + nightMode = new Date().getHours() < 7; + + Dungeon.level = level; + Actor.init(); + + Actor respawner = level.respawner(); + if (respawner != null) { + Actor.add( level.respawner() ); + } + + hero.pos = pos != -1 ? pos : level.exit; + + Light light = hero.buff( Light.class ); + hero.viewDistance = light == null ? level.viewDistance : Math.max( Light.DISTANCE, level.viewDistance ); + + observe(); + } + + public static boolean posNeeded() { + int[] quota = {4, 2, 9, 4, 14, 6, 19, 8, 24, 9}; + return chance( quota, potionOfStrength ); + } + + public static boolean soeNeeded() { + int[] quota = {5, 3, 10, 6, 15, 9, 20, 12, 25, 13}; + return chance( quota, scrollsOfUpgrade ); + } + + private static boolean chance( int[] quota, int number ) { + + for (int i=0; i < quota.length; i += 2) { + int qDepth = quota[i]; + if (depth <= qDepth) { + int qNumber = quota[i + 1]; + return Random.Float() < (float)(qNumber - number) / (qDepth - depth + 1); + } + } + + return false; + } + + public static boolean asNeeded() { + return Random.Int( 12 * (1 + arcaneStyli) ) < depth; + } + + private static final String RG_GAME_FILE = "game.dat"; + private static final String RG_DEPTH_FILE = "depth%d.dat"; + + private static final String WR_GAME_FILE = "warrior.dat"; + private static final String WR_DEPTH_FILE = "warrior%d.dat"; + + private static final String MG_GAME_FILE = "mage.dat"; + private static final String MG_DEPTH_FILE = "mage%d.dat"; + + private static final String RN_GAME_FILE = "ranger.dat"; + private static final String RN_DEPTH_FILE = "ranger%d.dat"; + + private static final String VERSION = "version"; + private static final String HERO = "hero"; + private static final String GOLD = "gold"; + private static final String DEPTH = "depth"; + private static final String QUICKSLOT = "quickslot"; + private static final String LEVEL = "level"; + private static final String POS = "potionsOfStrength"; + private static final String SOU = "scrollsOfEnhancement"; + private static final String AS = "arcaneStyli"; + private static final String DV = "dewVial"; + private static final String WT = "transmutation"; + private static final String CHAPTERS = "chapters"; + private static final String QUESTS = "quests"; + private static final String BADGES = "badges"; + + public static String gameFile( HeroClass cl ) { + switch (cl) { + case WARRIOR: + return WR_GAME_FILE; + case MAGE: + return MG_GAME_FILE; + case HUNTRESS: + return RN_GAME_FILE; + default: + return RG_GAME_FILE; + } + } + + private static String depthFile( HeroClass cl ) { + switch (cl) { + case WARRIOR: + return WR_DEPTH_FILE; + case MAGE: + return MG_DEPTH_FILE; + case HUNTRESS: + return RN_DEPTH_FILE; + default: + return RG_DEPTH_FILE; + } + } + + public static void saveGame( String fileName ) throws IOException { + try { + Bundle bundle = new Bundle(); + + bundle.put( VERSION, Game.version ); + bundle.put( HERO, hero ); + bundle.put( GOLD, gold ); + bundle.put( DEPTH, depth ); + + bundle.put( POS, potionOfStrength ); + bundle.put( SOU, scrollsOfUpgrade ); + bundle.put( AS, arcaneStyli ); + bundle.put( DV, dewVial ); + bundle.put( WT, transmutation ); + + int count = 0; + int ids[] = new int[chapters.size()]; + for (Integer id : chapters) { + ids[count++] = id; + } + bundle.put( CHAPTERS, ids ); + + Bundle quests = new Bundle(); + Ghost .Quest.storeInBundle( quests ); + Wandmaker .Quest.storeInBundle( quests ); + Blacksmith .Quest.storeInBundle( quests ); + Imp .Quest.storeInBundle( quests ); + bundle.put( QUESTS, quests ); + + Room.storeRoomsInBundle( bundle ); + + Statistics.storeInBundle( bundle ); + Journal.storeInBundle( bundle ); + + if (quickslot instanceof Class) { + bundle.put( QUICKSLOT, ((Class)quickslot).getName() ); + } + + Scroll.save( bundle ); + Potion.save( bundle ); + Wand.save( bundle ); + Ring.save( bundle ); + + Bundle badges = new Bundle(); + Badges.saveLocal( badges ); + bundle.put( BADGES, badges ); + + OutputStream output = Game.instance.openFileOutput( fileName, Game.MODE_PRIVATE ); + Bundle.write( bundle, output ); + output.close(); + + } catch (Exception e) { + + GamesInProgress.setUnknown( hero.heroClass ); + } + } + + public static void saveLevel() throws IOException { + Bundle bundle = new Bundle(); + bundle.put( LEVEL, level ); + + OutputStream output = Game.instance.openFileOutput( + Utils.format( depthFile( hero.heroClass ), depth ), Game.MODE_PRIVATE ); + Bundle.write( bundle, output ); + output.close(); + } + + public static void saveAll() throws IOException { + if (hero.isAlive()) { + + Actor.fixTime(); + saveGame( gameFile( hero.heroClass ) ); + saveLevel(); + + GamesInProgress.set( + hero.heroClass, + depth, + hero.lvl, + hero.belongings.armor != null ? hero.belongings.armor.tier : 0 ); + + } else if (WndResurrect.instance != null) { + + WndResurrect.instance.hide(); + Hero.reallyDie( WndResurrect.causeOfDeath ); + + } + } + + public static void loadGame( HeroClass cl ) throws IOException { + loadGame( gameFile( cl ), true ); + } + + public static void loadGame( String fileName ) throws IOException { + loadGame( fileName, false ); + } + + public static void loadGame( String fileName, boolean fullLoad ) throws IOException { + + Bundle bundle = gameBundle( fileName ); + + Dungeon.level = null; + Dungeon.depth = -1; + + if (fullLoad) { + PathFinder.setMapSize( Level.WIDTH, Level.HEIGHT ); + } + + Scroll.restore( bundle ); + Potion.restore( bundle ); + Wand.restore( bundle ); + Ring.restore( bundle ); + + potionOfStrength = bundle.getInt( POS ); + scrollsOfUpgrade = bundle.getInt( SOU ); + arcaneStyli = bundle.getInt( AS ); + dewVial = bundle.getBoolean( DV ); + transmutation = bundle.getInt( WT ); + + if (fullLoad) { + chapters = new HashSet(); + int ids[] = bundle.getIntArray( CHAPTERS ); + if (ids != null) { + for (int id : ids) { + chapters.add( id ); + } + } + + Bundle quests = bundle.getBundle( QUESTS ); + if (!quests.isNull()) { + Ghost.Quest.restoreFromBundle( quests ); + Wandmaker.Quest.restoreFromBundle( quests ); + Blacksmith.Quest.restoreFromBundle( quests ); + Imp.Quest.restoreFromBundle( quests ); + } else { + Ghost.Quest.reset(); + Wandmaker.Quest.reset(); + Blacksmith.Quest.reset(); + Imp.Quest.reset(); + } + + Room.restoreRoomsFromBundle( bundle ); + } + + Bundle badges = bundle.getBundle( BADGES ); + if (!badges.isNull()) { + Badges.loadLocal( badges ); + } else { + Badges.reset(); + } + + String qsClass = bundle.getString( QUICKSLOT ); + if (qsClass != null) { + try { + quickslot = Class.forName( qsClass ); + } catch (ClassNotFoundException e) { + } + } else { + quickslot = null; + } + + @SuppressWarnings("unused") + String version = bundle.getString( VERSION ); + + hero = null; + hero = (Hero)bundle.get( HERO ); + + gold = bundle.getInt( GOLD ); + depth = bundle.getInt( DEPTH ); + + Statistics.restoreFromBundle( bundle ); + Journal.restoreFromBundle( bundle ); + } + + public static Level loadLevel( HeroClass cl ) throws IOException { + + Dungeon.level = null; + Actor.clear(); + + InputStream input = Game.instance.openFileInput( Utils.format( depthFile( cl ), depth ) ) ; + Bundle bundle = Bundle.read( input ); + input.close(); + + return (Level)bundle.get( "level" ); + } + + public static void deleteGame( HeroClass cl, boolean deleteLevels ) { + + Game.instance.deleteFile( gameFile( cl ) ); + + if (deleteLevels) { + int depth = 1; + while (Game.instance.deleteFile( Utils.format( depthFile( cl ), depth ) )) { + depth++; + } + } + + GamesInProgress.delete( cl ); + } + + public static Bundle gameBundle( String fileName ) throws IOException { + + InputStream input = Game.instance.openFileInput( fileName ); + Bundle bundle = Bundle.read( input ); + input.close(); + + return bundle; + } + + public static void preview( GamesInProgress.Info info, Bundle bundle ) { + info.depth = bundle.getInt( DEPTH ); + if (info.depth == -1) { + info.depth = bundle.getInt( "maxDepth" ); // <-- It has to be refactored! + } + Hero.preview( info, bundle.getBundle( HERO ) ); + } + + public static void fail( String desc ) { + resultDescription = desc; + if (hero.belongings.getItem( Ankh.class ) == null) { + Rankings.INSTANCE.submit( false ); + } + } + + public static void win( String desc ) { + resultDescription = desc; + Rankings.INSTANCE.submit( true ); + } + + public static void observe() { + + if (level == null) { + return; + } + + level.updateFieldOfView( hero ); + System.arraycopy( Level.fieldOfView, 0, visible, 0, visible.length ); + + BArray.or( level.visited, visible, level.visited ); + + GameScene.afterObserve(); + } + + private static boolean[] passable = new boolean[Level.LENGTH]; + + public static int findPath( Char ch, int from, int to, boolean pass[], boolean[] visible ) { + + if (Level.adjacent( from, to )) { + return Actor.findChar( to ) == null && (pass[to] || Level.avoid[to]) ? to : -1; + } + + if (ch.flying || ch.buff( Amok.class ) != null) { + BArray.or( pass, Level.avoid, passable ); + } else { + System.arraycopy( pass, 0, passable, 0, Level.LENGTH ); + } + + for (Actor actor : Actor.all()) { + if (actor instanceof Char) { + int pos = ((Char)actor).pos; + if (visible[pos]) { + passable[pos] = false; + } + } + } + + return PathFinder.getStep( from, to, passable ); + + } + + public static int flee( Char ch, int cur, int from, boolean pass[], boolean[] visible ) { + + if (ch.flying) { + BArray.or( pass, Level.avoid, passable ); + } else { + System.arraycopy( pass, 0, passable, 0, Level.LENGTH ); + } + + for (Actor actor : Actor.all()) { + if (actor instanceof Char) { + int pos = ((Char)actor).pos; + if (visible[pos]) { + passable[pos] = false; + } + } + } + passable[cur] = true; + + return PathFinder.getStepBack( cur, from, passable ); + + } + +} diff --git a/src/com/watabou/pixeldungeon/DungeonTilemap.java b/src/com/watabou/pixeldungeon/DungeonTilemap.java new file mode 100644 index 0000000000..24d7af3ee9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/DungeonTilemap.java @@ -0,0 +1,94 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.Tilemap; +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Point; +import com.watabou.utils.PointF; + +public class DungeonTilemap extends Tilemap { + + public static final int SIZE = 16; + + private static DungeonTilemap instance; + + public DungeonTilemap() { + super( + Dungeon.level.tilesTex(), + new TextureFilm( Dungeon.level.tilesTex(), SIZE, SIZE ) ); + map( Dungeon.level.map, Level.WIDTH ); + + instance = this; + } + + public int screenToTile( int x, int y ) { + Point p = camera().screenToCamera( x, y ). + offset( this.point().negate() ). + invScale( SIZE ). + floor(); + return p.x >= 0 && p.x < Level.WIDTH && p.y >= 0 && p.y < Level.HEIGHT ? p.x + p.y * Level.WIDTH : -1; + } + + @Override + public boolean overlapsPoint( float x, float y ) { + return true; + } + + public void discover( int pos, int oldValue ) { + + final Image tile = tile( oldValue ); + tile.point( tileToWorld( pos ) ); + + // For bright mode + tile.rm = tile.gm = tile.bm = rm; + tile.ra = tile.ga = tile.ba = ra; + parent.add( tile ); + + parent.add( new AlphaTweener( tile, 0, 0.6f ) { + protected void onComplete() { + tile.killAndErase(); + killAndErase(); + }; + } ); + } + + public static PointF tileToWorld( int pos ) { + return new PointF( pos % Level.WIDTH, pos / Level.WIDTH ).scale( SIZE ); + } + + public static PointF tileCenterToWorld( int pos ) { + return new PointF( + (pos % Level.WIDTH + 0.5f) * SIZE, + (pos / Level.WIDTH + 0.5f) * SIZE ); + } + + public static Image tile( int index ) { + Image img = new Image( instance.texture ); + img.frame( instance.tileset.get( index ) ); + return img; + } + + @Override + public boolean overlapsScreenPoint( int x, int y ) { + return true; + } +} diff --git a/src/com/watabou/pixeldungeon/FogOfWar.java b/src/com/watabou/pixeldungeon/FogOfWar.java new file mode 100644 index 0000000000..39d51b7b31 --- /dev/null +++ b/src/com/watabou/pixeldungeon/FogOfWar.java @@ -0,0 +1,121 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.util.Arrays; + +import android.graphics.Bitmap; + +import com.watabou.gltextures.SmartTexture; +import com.watabou.gltextures.TextureCache; +import com.watabou.glwrap.Texture; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class FogOfWar extends Image { + + private static final int VISIBLE = 0x00000000; + private static final int VISITED = 0xcc111111; + private static final int MAPPED = 0xcc442211; + private static final int INVISIBLE = 0xFF000000; + + private int[] pixels; + + private int pWidth; + private int pHeight; + + private int width2; + private int height2; + + public FogOfWar( int mapWidth, int mapHeight ) { + + super(); + + pWidth = mapWidth + 1; + pHeight = mapHeight + 1; + + width2 = 1; + while (width2 < pWidth) { + width2 <<= 1; + } + + height2 = 1; + while (height2 < pHeight) { + height2 <<= 1; + } + + float size = DungeonTilemap.SIZE; + width = width2 * size; + height = height2 * size; + + texture( new FogTexture() ); + + scale.set( + DungeonTilemap.SIZE, + DungeonTilemap.SIZE ); + + x = y = -size / 2; + } + + public void updateVisibility( boolean[] visible, boolean[] visited, boolean[] mapped ) { + + if (pixels == null) { + pixels = new int[width2 * height2]; + Arrays.fill( pixels, INVISIBLE ); + } + + for (int i=1; i < pHeight - 1; i++) { + int pos = (pWidth - 1) * i; + for (int j=1; j < pWidth - 1; j++) { + pos++; + int c = INVISIBLE; + if (visible[pos] && visible[pos - (pWidth - 1)] && + visible[pos - 1] && visible[pos - (pWidth - 1) - 1]) { + c = VISIBLE; + } else + if (visited[pos] && visited[pos - (pWidth - 1)] && + visited[pos - 1] && visited[pos - (pWidth - 1) - 1]) { + c = VISITED; + } + else + if (mapped[pos] && mapped[pos - (pWidth - 1)] && + mapped[pos - 1] && mapped[pos - (pWidth - 1) - 1]) { + c = MAPPED; + } + pixels[i * width2 + j] = c; + } + } + + texture.pixels( width2, height2, pixels ); + } + + private class FogTexture extends SmartTexture { + + public FogTexture() { + super( Bitmap.createBitmap( width2, height2, Bitmap.Config.ARGB_8888 ) ); + filter( Texture.LINEAR, Texture.LINEAR ); + TextureCache.add( FogOfWar.class, this ); + } + + @Override + public void reload() { + super.reload(); + GameScene.afterObserve(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/GamesInProgress.java b/src/com/watabou/pixeldungeon/GamesInProgress.java new file mode 100644 index 0000000000..ad5659ea6d --- /dev/null +++ b/src/com/watabou/pixeldungeon/GamesInProgress.java @@ -0,0 +1,75 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.util.HashMap; + +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.utils.Bundle; + +public class GamesInProgress { + + private static HashMap state = new HashMap(); + + public static Info check( HeroClass cl ) { + + if (state.containsKey( cl )) { + + return state.get( cl ); + + } else { + + Info info; + try { + + Bundle bundle = Dungeon.gameBundle( Dungeon.gameFile( cl ) ); + info = new Info(); + Dungeon.preview( info, bundle ); + + } catch (Exception e) { + info = null; + } + + state.put( cl, info ); + return info; + + } + } + + public static void set( HeroClass cl, int depth, int level, int armor ) { + Info info = new Info(); + info.depth = depth; + info.level = level; + info.armor = armor; + state.put( cl, info ); + } + + public static void setUnknown( HeroClass cl ) { + state.remove( cl ); + } + + public static void delete( HeroClass cl ) { + state.put( cl, null ); + } + + public static class Info { + public int depth; + public int level; + public int armor; + } +} diff --git a/src/com/watabou/pixeldungeon/Journal.java b/src/com/watabou/pixeldungeon/Journal.java new file mode 100644 index 0000000000..3edf2b4188 --- /dev/null +++ b/src/com/watabou/pixeldungeon/Journal.java @@ -0,0 +1,122 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.util.ArrayList; + +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; + +public class Journal { + + public static enum Feature { + WELL_OF_HEALTH ( "Well of Health" ), + WELL_OF_AWARENESS ( "Well of Awareness" ), + WELL_OF_TRANSMUTATION ( "Well of Transmutation" ), + ALCHEMY ( "Alchemy pot" ), + GARDEN ( "Garden" ), + STATUE ( "Animated statue" ), + + GHOST ( "Sad ghost" ), + WANDMAKER ( "Old wandmaker" ), + TROLL ( "Troll blacksmith" ), + IMP ( "Ambitious imp" ); + + public String desc; + + private Feature( String desc ) { + this.desc = desc; + } + }; + + public static class Record implements Comparable, Bundlable { + + private static final String FEATURE = "feature"; + private static final String DEPTH = "depth"; + + public Feature feature; + public int depth; + + public Record() { + } + + public Record( Feature feature, int depth ) { + this.feature = feature; + this.depth = depth; + } + + @Override + public int compareTo( Record another ) { + return another.depth - depth; + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + feature = Feature.valueOf( bundle.getString( FEATURE ) ); + depth = bundle.getInt( DEPTH ); + } + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( FEATURE, feature.toString() ); + bundle.put( DEPTH, depth ); + } + } + + public static ArrayList records; + + public static void reset() { + records = new ArrayList(); + } + + private static final String JOURNAL = "journal"; + + public static void storeInBundle( Bundle bundle ) { + bundle.put( JOURNAL, records ); + } + + public static void restoreFromBundle( Bundle bundle ) { + records = new ArrayList(); + for (Bundlable rec : bundle.getCollection( JOURNAL ) ) { + records.add( (Record) rec ); + } + } + + public static void add( Feature feature ) { + int size = records.size(); + for (int i=0; i < size; i++) { + Record rec = records.get( i ); + if (rec.feature == feature && rec.depth == Dungeon.depth) { + return; + } + } + + records.add( new Record( feature, Dungeon.depth ) ); + } + + public static void remove( Feature feature ) { + int size = records.size(); + for (int i=0; i < size; i++) { + Record rec = records.get( i ); + if (rec.feature == feature && rec.depth == Dungeon.depth) { + records.remove( i ); + return; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/PixelDungeon.java b/src/com/watabou/pixeldungeon/PixelDungeon.java new file mode 100644 index 0000000000..6f5af9c539 --- /dev/null +++ b/src/com/watabou/pixeldungeon/PixelDungeon.java @@ -0,0 +1,206 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.Display; + +import com.watabou.noosa.Game; +import com.watabou.noosa.audio.Music; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.TitleScene; + +public class PixelDungeon extends Game { + + public PixelDungeon() { + super( TitleScene.class ); + + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade.class, + "com.watabou.pixeldungeon.items.scrolls.ScrollOfEnhancement" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.actors.blobs.WaterOfHealth.class, + "com.watabou.pixeldungeon.actors.blobs.Light" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.rings.RingOfMending.class, + "com.watabou.pixeldungeon.items.rings.RingOfRejuvenation" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.wands.WandOfTelekinesis.class, + "com.watabou.pixeldungeon.items.wands.WandOfTelekenesis" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.actors.blobs.Foliage.class, + "com.watabou.pixeldungeon.actors.blobs.Blooming" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.actors.buffs.Shadows.class, + "com.watabou.pixeldungeon.actors.buffs.Rejuvenation" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast.class, + "com.watabou.pixeldungeon.items.scrolls.ScrollOfNuclearBlast" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.actors.hero.Hero.class, + "com.watabou.pixeldungeon.actors.Hero" ); + // com.watabou.utils.Bundle.addAlias( + // com.watabou.pixeldungeon.items.weapon.missiles.Javelin.class, + // "com.watabou.pixeldungeon.items.weapon.missiles.Boomerang" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.actors.mobs.npcs.Shopkeeper.class, + "com.watabou.pixeldungeon.actors.mobs.Shopkeeper" ); + // 1.6.1 + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.quest.DriedRose.class, + "com.watabou.pixeldungeon.items.DriedRose" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.actors.mobs.npcs.MirrorImage.class, + "com.watabou.pixeldungeon.items.scrolls.ScrollOfMirrorImage.MirrorImage" ); + // 1.6.4 + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.rings.RingOfElements.class, + "com.watabou.pixeldungeon.items.rings.RingOfCleansing" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.rings.RingOfElements.class, + "com.watabou.pixeldungeon.items.rings.RingOfResistance" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.weapon.missiles.Boomerang.class, + "com.watabou.pixeldungeon.items.weapon.missiles.RangersBoomerang" ); + com.watabou.utils.Bundle.addAlias( + com.watabou.pixeldungeon.items.rings.RingOfPower.class, + "com.watabou.pixeldungeon.items.rings.RingOfEnergy" ); + } + + @SuppressWarnings("deprecation") + @Override + protected void onCreate( Bundle savedInstanceState ) { + super.onCreate( savedInstanceState ); + + /* if (android.os.Build.VERSION.SDK_INT >= 19) { + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY ); + }*/ + + Display display = instance.getWindowManager().getDefaultDisplay(); + boolean landscape = display.getWidth() > display.getHeight(); + + if (Preferences.INSTANCE.getBoolean( Preferences.KEY_LANDSCAPE, false ) != landscape) { + landscape( !landscape ); + } + + Music.INSTANCE.enable( music() ); + Sample.INSTANCE.enable( soundFx() ); + } + + /* + * ---> Prefernces + */ + + public static void landscape( boolean value ) { + Game.instance.setRequestedOrientation( value ? + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ); + Preferences.INSTANCE.put( Preferences.KEY_LANDSCAPE, value ); + } + + public static boolean landscape() { + return width > height; + } + + public static void scaleUp( boolean value ) { + Preferences.INSTANCE.put( Preferences.KEY_SCALE_UP, value ); + switchScene( TitleScene.class ); + } + + public static boolean scaleUp() { + return Preferences.INSTANCE.getBoolean( Preferences.KEY_SCALE_UP, true ); + } + + public static void zoom( int value ) { + Preferences.INSTANCE.put( Preferences.KEY_ZOOM, value ); + } + + public static int zoom() { + return Preferences.INSTANCE.getInt( Preferences.KEY_ZOOM, 0 ); + } + + public static void music( boolean value ) { + Music.INSTANCE.enable( value ); + Preferences.INSTANCE.put( Preferences.KEY_MUSIC, value ); + } + + public static boolean music() { + return Preferences.INSTANCE.getBoolean( Preferences.KEY_MUSIC, true ); + } + + public static void soundFx( boolean value ) { + Sample.INSTANCE.enable( value ); + Preferences.INSTANCE.put( Preferences.KEY_SOUND_FX, value ); + } + + public static boolean soundFx() { + return Preferences.INSTANCE.getBoolean( Preferences.KEY_SOUND_FX, true ); + } + + public static void brightness( boolean value ) { + Preferences.INSTANCE.put( Preferences.KEY_BRIGHTNESS, value ); + if (scene() instanceof GameScene) { + ((GameScene)scene()).brightness( value ); + } + } + + public static boolean brightness() { + return Preferences.INSTANCE.getBoolean( Preferences.KEY_BRIGHTNESS, false ); + } + + public static void donated( String value ) { + Preferences.INSTANCE.put( Preferences.KEY_DONATED, value ); + } + + public static String donated() { + return Preferences.INSTANCE.getString( Preferences.KEY_DONATED, "" ); + } + + public static void lastClass( int value ) { + Preferences.INSTANCE.put( Preferences.KEY_LAST_CLASS, value ); + } + + public static int lastClass() { + return Preferences.INSTANCE.getInt( Preferences.KEY_LAST_CLASS, 0 ); + } + + public static void intro( boolean value ) { + Preferences.INSTANCE.put( Preferences.KEY_INTRO, value ); + } + + public static boolean intro() { + return Preferences.INSTANCE.getBoolean( Preferences.KEY_INTRO, true ); + } + + /* + * <--- Preferences + */ + + public static void reportException( Exception e ) { + Log.e( "PD", Log.getStackTraceString( e ) ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/Preferences.java b/src/com/watabou/pixeldungeon/Preferences.java new file mode 100644 index 0000000000..7fbfdbb798 --- /dev/null +++ b/src/com/watabou/pixeldungeon/Preferences.java @@ -0,0 +1,70 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import com.watabou.noosa.Game; + +import android.content.SharedPreferences; + +enum Preferences { + + INSTANCE; + + public static final String KEY_LANDSCAPE = "landscape"; + public static final String KEY_SCALE_UP = "scaleup"; + public static final String KEY_MUSIC = "music"; + public static final String KEY_SOUND_FX = "soundfx"; + public static final String KEY_ZOOM = "zoom"; + public static final String KEY_LAST_CLASS = "last_class"; + public static final String KEY_DONATED = "donated"; + public static final String KEY_INTRO = "intro"; + public static final String KEY_BRIGHTNESS = "brightness"; + + private SharedPreferences prefs; + + private SharedPreferences get() { + if (prefs == null) { + prefs = Game.instance.getPreferences( Game.MODE_PRIVATE ); + } + return prefs; + } + + int getInt( String key, int defValue ) { + return get().getInt( key, defValue ); + } + + boolean getBoolean( String key, boolean defValue ) { + return get().getBoolean( key, defValue ); + } + + String getString( String key, String defValue ) { + return get().getString( key, defValue ); + } + + void put( String key, int value ) { + get().edit().putInt( key, value ).commit(); + } + + void put( String key, boolean value ) { + get().edit().putBoolean( key, value ).commit(); + } + + void put( String key, String value ) { + get().edit().putString( key, value ).commit(); + } +} diff --git a/src/com/watabou/pixeldungeon/Rankings.java b/src/com/watabou/pixeldungeon/Rankings.java new file mode 100644 index 0000000000..638de2c67e --- /dev/null +++ b/src/com/watabou/pixeldungeon/Rankings.java @@ -0,0 +1,195 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.SystemTime; + +public enum Rankings { + + INSTANCE; + + public static final int TABLE_SIZE = 6; + + public static final String RANKINGS_FILE = "rankings.dat"; + public static final String DETAILS_FILE = "game_%d.dat"; + + public ArrayList records; + public int lastRecord; + public int totalNumber; + + public void submit( boolean win ) { + + load(); + + Record rec = new Record(); + + rec.info = Dungeon.resultDescription; + rec.win = win; + rec.heroClass = Dungeon.hero.heroClass; + rec.armorTier = Dungeon.hero.tier(); + rec.score = score( win ); + + String gameFile = Utils.format( DETAILS_FILE, SystemTime.now ); + try { + Dungeon.saveGame( gameFile ); + rec.gameFile = gameFile; + } catch (IOException e) { + rec.gameFile = ""; + } + + records.add( rec ); + + Collections.sort( records, scoreComparator ); + + lastRecord = records.indexOf( rec ); + int size = records.size(); + if (size > TABLE_SIZE) { + + Record removedGame; + if (lastRecord == size - 1) { + removedGame = records.remove( size - 2 ); + lastRecord--; + } else { + removedGame = records.remove( size - 1 ); + } + + if (removedGame.gameFile.length() > 0) { + Game.instance.deleteFile( removedGame.gameFile ); + } + } + + totalNumber++; + + Badges.validateGamesPlayed(); + + save(); + } + + private int score( boolean win ) { + return (Statistics.goldCollected + Dungeon.hero.lvl * Dungeon.depth * 100) * (win ? 2 : 1); + } + + private static final String RECORDS = "records"; + private static final String LATEST = "latest"; + private static final String TOTAL = "total"; + + public void save() { + Bundle bundle = new Bundle(); + bundle.put( RECORDS, records ); + bundle.put( LATEST, lastRecord ); + bundle.put( TOTAL, totalNumber ); + + try { + OutputStream output = Game.instance.openFileOutput( RANKINGS_FILE, Game.MODE_PRIVATE ); + Bundle.write( bundle, output ); + output.close(); + } catch (Exception e) { + } + } + + public void load() { + + if (records != null) { + return; + } + + records = new ArrayList(); + + try { + InputStream input = Game.instance.openFileInput( RANKINGS_FILE ); + Bundle bundle = Bundle.read( input ); + input.close(); + + for (Bundlable record : bundle.getCollection( RECORDS )) { + records.add( (Record)record ); + } + lastRecord = bundle.getInt( LATEST ); + + totalNumber = bundle.getInt( TOTAL ); + if (totalNumber == 0) { + totalNumber = records.size(); + } + + } catch (Exception e) { + } + } + + public static class Record implements Bundlable { + + private static final String REASON = "reason"; + private static final String WIN = "win"; + private static final String SCORE = "score"; + private static final String TIER = "tier"; + private static final String GAME = "gameFile"; + + public String info; + public boolean win; + + public HeroClass heroClass; + public int armorTier; + + public int score; + + public String gameFile; + + @Override + public void restoreFromBundle( Bundle bundle ) { + + info = bundle.getString( REASON ); + win = bundle.getBoolean( WIN ); + score = bundle.getInt( SCORE ); + + heroClass = HeroClass.restoreInBundle( bundle ); + armorTier = bundle.getInt( TIER ); + + gameFile = bundle.getString( GAME ); + } + + @Override + public void storeInBundle( Bundle bundle ) { + + bundle.put( REASON, info ); + bundle.put( WIN, win ); + bundle.put( SCORE, score ); + + heroClass.storeInBundle( bundle ); + bundle.put( TIER, armorTier ); + + bundle.put( GAME, gameFile ); + } + } + + private static final Comparator scoreComparator = new Comparator() { + @Override + public int compare( Record lhs, Record rhs ) { + return (int)Math.signum( rhs.score - lhs.score ); + } + }; +} diff --git a/src/com/watabou/pixeldungeon/ResultDescriptions.java b/src/com/watabou/pixeldungeon/ResultDescriptions.java new file mode 100644 index 0000000000..27248a0826 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ResultDescriptions.java @@ -0,0 +1,43 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +public class ResultDescriptions { + + // Mobs + public static final String MOB = "Killed by %s on level %d"; + public static final String BOSS = "Killed by the %s on level %d"; + + // Items + public static final String WAND = "Killed by your own %s on level %d"; + public static final String GLYPH = "Killed by the %s on level %d"; + + // Dungeon features + public static final String TRAP = "Killed by discharge of %s on level %d"; + + // Debuffs & blobs + public static final String BURNING = "Burned to death on level %d"; + public static final String HUNGER = "Starved to death on level %d"; + public static final String POISON = "Died from poison on level %d"; + public static final String GAS = "Died from toxic gas on level %d"; + public static final String BLEEDING = "Bled to death on level %d"; + public static final String OOZE = "Killed by a caustic ooze on level %d"; + public static final String FALL = "Fell to death on level %d"; + + public static final String WIN = "Obtained the Amulet of Yendor"; +} diff --git a/src/com/watabou/pixeldungeon/Statistics.java b/src/com/watabou/pixeldungeon/Statistics.java new file mode 100644 index 0000000000..2b74531774 --- /dev/null +++ b/src/com/watabou/pixeldungeon/Statistics.java @@ -0,0 +1,96 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon; + +import com.watabou.utils.Bundle; + +public class Statistics { + + public static int goldCollected; + public static int deepestFloor; + public static int enemiesSlain; + public static int foodEaten; + public static int potionsCooked; + public static int piranhasKilled; + public static int nightHunt; + public static int ankhsUsed; + + public static float duration; + + public static boolean qualifiedForNoKilling = false; + public static boolean completedWithNoKilling = false; + + public static boolean amuletObtained = false; + + public static void reset() { + + goldCollected = 0; + deepestFloor = 0; + enemiesSlain = 0; + foodEaten = 0; + potionsCooked = 0; + piranhasKilled = 0; + nightHunt = 0; + ankhsUsed = 0; + + duration = 0; + + qualifiedForNoKilling = false; + + amuletObtained = false; + + } + + private static final String GOLD = "score"; + private static final String DEEPEST = "maxDepth"; + private static final String SLAIN = "enemiesSlain"; + private static final String FOOD = "foodEaten"; + private static final String ALCHEMY = "potionsCooked"; + private static final String PIRANHAS = "priranhas"; + private static final String NIGHT = "nightHunt"; + private static final String ANKHS = "ankhsUsed"; + private static final String DURATION = "duration"; + private static final String AMULET = "amuletObtained"; + + public static void storeInBundle( Bundle bundle ) { + bundle.put( GOLD, goldCollected ); + bundle.put( DEEPEST, deepestFloor ); + bundle.put( SLAIN, enemiesSlain ); + bundle.put( FOOD, foodEaten ); + bundle.put( ALCHEMY, potionsCooked ); + bundle.put( PIRANHAS, piranhasKilled ); + bundle.put( NIGHT, nightHunt ); + bundle.put( ANKHS, ankhsUsed ); + bundle.put( DURATION, duration ); + bundle.put( AMULET, amuletObtained ); + } + + public static void restoreFromBundle( Bundle bundle ) { + goldCollected = bundle.getInt( GOLD ); + deepestFloor = bundle.getInt( DEEPEST ); + enemiesSlain = bundle.getInt( SLAIN ); + foodEaten = bundle.getInt( FOOD ); + potionsCooked = bundle.getInt( ALCHEMY ); + piranhasKilled = bundle.getInt( PIRANHAS ); + nightHunt = bundle.getInt( NIGHT ); + ankhsUsed = bundle.getInt( ANKHS ); + duration = bundle.getFloat( DURATION ); + amuletObtained = bundle.getBoolean( AMULET ); + } + +} diff --git a/src/com/watabou/pixeldungeon/actors/Actor.java b/src/com/watabou/pixeldungeon/actors/Actor.java new file mode 100644 index 0000000000..ee6cd9cc74 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/Actor.java @@ -0,0 +1,219 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors; + +import java.util.Arrays; +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; + +public abstract class Actor implements Bundlable { + + public static final float TICK = 1f; + + private float time; + + protected abstract boolean act(); + + protected void spend( float time ) { + this.time += time; + } + + protected void postpone( float time ) { + if (this.time < now + time) { + this.time = now + time; + } + } + + protected float cooldown() { + return time - now; + } + + protected void diactivate() { + time = Float.MAX_VALUE; + } + + protected void onAdd() {} + + protected void onRemove() {} + + private static final String TIME = "time"; + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( TIME, time ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + time = bundle.getFloat( TIME ); + } + + // ********************** + // *** Static members *** + + private static HashSet all = new HashSet(); + private static Actor current; + + private static float now = 0; + + private static Char[] chars = new Char[Level.LENGTH]; + + public static void clear() { + + now = 0; + + Arrays.fill( chars, null ); + all.clear(); + } + + public static void fixTime() { + + if (Dungeon.hero != null && all.contains( Dungeon.hero )) { + Statistics.duration += now; + } + + float min = Float.MAX_VALUE; + for (Actor a : all) { + if (a.time < min) { + min = a.time; + } + } + for (Actor a : all) { + a.time -= min; + } + now = 0; + } + + public static void init() { + + addDelayed( Dungeon.hero, -Float.MIN_VALUE ); + + for (Mob mob : Dungeon.level.mobs) { + add( mob ); + } + + for (Blob blob : Dungeon.level.blobs.values()) { + add( blob ); + } + + current = null; + } + + public static void occupyCell( Char ch ) { + chars[ch.pos] = ch; + } + + public static void freeCell( int pos ) { + chars[pos] = null; + } + + protected static void next() { + current = null; + } + + public static void process() { + + if (current != null) { + return; + } + + boolean doNext; + + do { + now = Float.MAX_VALUE; + current = null; + + Arrays.fill( chars, null ); + + for (Actor actor : all) { + if (actor.time < now) { + now = actor.time; + current = actor; + } + + if (actor instanceof Char) { + Char ch = (Char)actor; + chars[ch.pos] = ch; + } + } + + if (current != null) { + doNext = current.act(); + if (doNext && !Dungeon.hero.isAlive()) { + doNext = false; + current = null; + } + } else { + doNext = false; + } + + } while (doNext); + } + + public static void add( Actor actor ) { + add( actor, now ); + } + + public static void addDelayed( Actor actor, float delay ) { + add( actor, now + delay ); + } + + private static void add( Actor actor, float time ) { + + if (all.contains( actor )) { + return; + } + + all.add( actor ); + actor.time += time; // (+=) => (=) ? + actor.onAdd(); + + if (actor instanceof Char) { + Char ch = (Char)actor; + chars[ch.pos] = ch; + for (Buff buff : ch.buffs()) { + all.add( buff ); + buff.onAdd(); + } + } + } + + public static void remove( Actor actor ) { + + if (actor != null) { + all.remove( actor ); + actor.onRemove(); + } + } + + public static Char findChar( int pos ) { + return chars[pos]; + } + + public static HashSet all() { + return all; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/Char.java b/src/com/watabou/pixeldungeon/actors/Char.java new file mode 100644 index 0000000000..ba3b862b43 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/Char.java @@ -0,0 +1,493 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors; + +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.actors.buffs.Frost; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.buffs.Shadows; +import com.watabou.pixeldungeon.actors.buffs.Sleep; +import com.watabou.pixeldungeon.actors.buffs.Speed; +import com.watabou.pixeldungeon.actors.buffs.Levitation; +import com.watabou.pixeldungeon.actors.buffs.MindVision; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Slow; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.PoisonParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.levels.features.Door; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public abstract class Char extends Actor { + + protected static final String TXT_HIT = "%s hit %s"; + protected static final String TXT_KILL = "%s killed you..."; + protected static final String TXT_DEFEAT = "%s defeated %s"; + + private static final String TXT_YOU_MISSED = "%s %s your attack"; + private static final String TXT_SMB_MISSED = "%s %s %s's attack"; + + private static final String TXT_OUT_OF_PARALYSIS = "The pain snapped %s out of paralysis"; + + public int pos = 0; + + public CharSprite sprite; + + public String name = "mob"; + + public int HT; + public int HP; + + protected float baseSpeed = 1; + + public boolean paralysed = false; + public boolean pacified = false; + public boolean rooted = false; + public boolean flying = false; + public int invisible = 0; + + public int viewDistance = 8; + + private HashSet buffs = new HashSet(); + + @Override + protected boolean act() { + Dungeon.level.updateFieldOfView( this ); + return false; + } + + private static final String POS = "pos"; + private static final String TAG_HP = "HP"; + private static final String TAG_HT = "HT"; + private static final String BUFFS = "buffs"; + + @Override + public void storeInBundle( Bundle bundle ) { + + super.storeInBundle( bundle ); + + bundle.put( POS, pos ); + bundle.put( TAG_HP, HP ); + bundle.put( TAG_HT, HT ); + bundle.put( BUFFS, buffs ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + + super.restoreFromBundle( bundle ); + + pos = bundle.getInt( POS ); + HP = bundle.getInt( TAG_HP ); + HT = bundle.getInt( TAG_HT ); + + for (Bundlable b : bundle.getCollection( BUFFS )) { + if (b != null) { + ((Buff)b).attachTo( this ); + } + } + } + + public boolean attack( Char enemy ) { + + boolean visibleFight = Dungeon.visible[pos] || Dungeon.visible[enemy.pos]; + + if (hit( this, enemy, false )) { + + if (visibleFight) { + GLog.i( TXT_HIT, name, enemy.name ); + } + + // Refactoring needed! + int dr = this instanceof Hero && ((Hero)this).usingRanged && ((Hero)this).subClass == HeroSubClass.SNIPER ? + 0 : Random.IntRange( 0, enemy.dr() ); + + int dmg = damageRoll(); + int effectiveDamage = Math.max( dmg - dr, 0 );; + + effectiveDamage = attackProc( enemy, effectiveDamage ); + effectiveDamage = enemy.defenseProc( this, effectiveDamage ); + enemy.damage( effectiveDamage, this ); + + if (visibleFight) { + Sample.INSTANCE.play( Assets.SND_HIT, 1, 1, Random.Float( 0.8f, 1.25f ) ); + } + + if (enemy == Dungeon.hero) { + Dungeon.hero.interrupt(); + } + + enemy.sprite.bloodBurstA( sprite.center(), effectiveDamage ); + enemy.sprite.flash(); + + if (!enemy.isAlive() && visibleFight) { + if (enemy == Dungeon.hero) { + + if (Dungeon.hero.killerGlyph != null) { + + Dungeon.fail( Utils.format( ResultDescriptions.GLYPH, Dungeon.hero.killerGlyph.name(), Dungeon.depth ) ); + GLog.n( TXT_KILL, Dungeon.hero.killerGlyph.name() ); + + } else { + if (Bestiary.isUnique( this )) { + Dungeon.fail( Utils.format( ResultDescriptions.BOSS, name, Dungeon.depth ) ); + } else { + Dungeon.fail( Utils.format( ResultDescriptions.MOB, + Utils.indefinite( name ), Dungeon.depth ) ); + } + + GLog.n( TXT_KILL, name ); + } + + } else { + GLog.i( TXT_DEFEAT, name, enemy.name ); + } + } + + return true; + + } else { + + if (visibleFight) { + String defense = enemy.defenseVerb(); + enemy.sprite.showStatus( CharSprite.NEUTRAL, defense ); + if (this == Dungeon.hero) { + GLog.i( TXT_YOU_MISSED, enemy.name, defense ); + } else { + GLog.i( TXT_SMB_MISSED, enemy.name, defense, name ); + } + + Sample.INSTANCE.play( Assets.SND_MISS ); + } + + return false; + + } + } + + public static boolean hit( Char attacker, Char defender, boolean magic ) { + float acuRoll = Random.Float( attacker.attackSkill( defender ) ); + float defRoll = Random.Float( defender.defenseSkill( attacker ) ); + return (magic ? acuRoll * 2 : acuRoll) >= defRoll; + } + + public int attackSkill( Char target ) { + return 0; + } + + public int defenseSkill( Char enemy ) { + return 0; + } + + public String defenseVerb() { + return "dodged"; + } + + public int dr() { + return 0; + } + + public int damageRoll() { + return 1; + } + + public int attackProc( Char enemy, int damage ) { + return damage; + } + + public int defenseProc( Char enemy, int damage ) { + return damage; + } + + public float speed() { + return buff( Cripple.class ) == null ? baseSpeed : baseSpeed * 0.5f; + } + + public void damage( int dmg, Object src ) { + + if (HP <= 0) { + return; + } + + Buff.detach( this, Frost.class ); + + Class srcClass = src.getClass(); + if (immunities().contains( srcClass )) { + dmg = 0; + } else if (resistances().contains( srcClass )) { + dmg = Random.IntRange( 0, dmg ); + } + + if (buff( Paralysis.class ) != null) { + if (Random.Int( dmg ) >= Random.Int( HP )) { + Buff.detach( this, Paralysis.class ); + if (Dungeon.visible[pos]) { + GLog.i( TXT_OUT_OF_PARALYSIS, name ); + } + } + } + + HP -= dmg; + if (dmg > 0 || src instanceof Char) { + sprite.showStatus( HP > HT / 2 ? + CharSprite.WARNING : + CharSprite.NEGATIVE, + Integer.toString( dmg ) ); + } + if (HP <= 0) { + die( src ); + } + } + + public void destroy() { + HP = 0; + Actor.remove( this ); + Actor.freeCell( pos ); + } + + public void die( Object src ) { + destroy(); + sprite.die(); + } + + public boolean isAlive() { + return HP > 0; + } + + @Override + protected void spend( float time ) { + + float timeScale = 1f; + if (buff( Slow.class ) != null) { + timeScale *= 0.5f; + } + if (buff( Speed.class ) != null) { + timeScale *= 2.0f; + } + + super.spend( time / timeScale ); + } + + public HashSet buffs() { + return buffs; + } + + @SuppressWarnings("unchecked") + public HashSet buffs( Class c ) { + HashSet filtered = new HashSet(); + for (Buff b : buffs) { + if (c.isInstance( b )) { + filtered.add( (T)b ); + } + } + return filtered; + } + + @SuppressWarnings("unchecked") + public T buff( Class c ) { + for (Buff b : buffs) { + if (c.isInstance( b )) { + return (T)b; + } + } + return null; + } + + + public void add( Buff buff ) { + + buffs.add( buff ); + Actor.add( buff ); + + if (sprite != null) { + if (buff instanceof Poison) { + + CellEmitter.center( pos ).burst( PoisonParticle.SPLASH, 5 ); + sprite.showStatus( CharSprite.NEGATIVE, "poisoned" ); + + } else if (buff instanceof Amok) { + + sprite.showStatus( CharSprite.NEGATIVE, "amok" ); + + } else if (buff instanceof Slow) { + + sprite.showStatus( CharSprite.NEGATIVE, "slowed" ); + + } else if (buff instanceof MindVision) { + + sprite.showStatus( CharSprite.POSITIVE, "mind" ); + sprite.showStatus( CharSprite.POSITIVE, "vision" ); + + } else if (buff instanceof Paralysis) { + + sprite.add( CharSprite.State.PARALYSED ); + sprite.showStatus( CharSprite.NEGATIVE, "paralysed" ); + + } else if (buff instanceof Terror) { + + sprite.showStatus( CharSprite.NEGATIVE, "frightened" ); + + } else if (buff instanceof Roots) { + + sprite.showStatus( CharSprite.NEGATIVE, "rooted" ); + + } else if (buff instanceof Cripple) { + + sprite.showStatus( CharSprite.NEGATIVE, "crippled" ); + + } else if (buff instanceof Bleeding) { + + sprite.showStatus( CharSprite.NEGATIVE, "bleeding" ); + + } else if (buff instanceof Sleep) { + sprite.idle(); + } + + else if (buff instanceof Burning) { + sprite.add( CharSprite.State.BURNING ); + } else if (buff instanceof Levitation) { + sprite.add( CharSprite.State.LEVITATING ); + } else if (buff instanceof Frost) { + sprite.add( CharSprite.State.FROZEN ); + } else if (buff instanceof Invisibility) { + if (!(buff instanceof Shadows)) { + sprite.showStatus( CharSprite.POSITIVE, "invisible" ); + } + sprite.add( CharSprite.State.INVISIBLE ); + } + } + } + + public void remove( Buff buff ) { + + buffs.remove( buff ); + Actor.remove( buff ); + + if (buff instanceof Burning) { + sprite.remove( CharSprite.State.BURNING ); + } else if (buff instanceof Levitation) { + sprite.remove( CharSprite.State.LEVITATING ); + } else if (buff instanceof Invisibility && invisible <= 0) { + sprite.remove( CharSprite.State.INVISIBLE ); + } else if (buff instanceof Paralysis) { + sprite.remove( CharSprite.State.PARALYSED ); + } else if (buff instanceof Frost) { + sprite.remove( CharSprite.State.FROZEN ); + } + } + + public void remove( Class buffClass ) { + for (Buff buff : buffs( buffClass )) { + remove( buff ); + } + } + + @Override + protected void onRemove() { + for (Buff buff : buffs.toArray( new Buff[0] )) { + buff.detach(); + } + } + + public void updateSpriteState() { + for (Buff buff:buffs) { + if (buff instanceof Burning) { + sprite.add( CharSprite.State.BURNING ); + } else if (buff instanceof Levitation) { + sprite.add( CharSprite.State.LEVITATING ); + } else if (buff instanceof Invisibility) { + sprite.add( CharSprite.State.INVISIBLE ); + } else if (buff instanceof Paralysis) { + sprite.add( CharSprite.State.PARALYSED ); + } else if (buff instanceof Frost) { + sprite.add( CharSprite.State.FROZEN ); + } else if (buff instanceof Light) { + sprite.add( CharSprite.State.ILLUMINATED ); + } + } + } + + public int stealth() { + return 0; + } + + public void move( int step ) { + if (Dungeon.level.map[pos] == Terrain.OPEN_DOOR) { + Door.leave( pos ); + } + + pos = step; + + if (flying && Dungeon.level.map[pos] == Terrain.DOOR) { + Door.enter( pos ); + } + + if (this != Dungeon.hero) { + sprite.visible = Dungeon.visible[pos]; + } + } + + public int distance( Char other ) { + return Level.distance( pos, other.pos ); + } + + public void onMotionComplete() { + next(); + } + + public void onAttackComplete() { + next(); + } + + public void onOperateComplete() { + next(); + } + + private static final HashSet> EMPTY = new HashSet>(); + + public HashSet> resistances() { + return EMPTY; + } + + public HashSet> immunities() { + return EMPTY; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Alchemy.java b/src/com/watabou/pixeldungeon/actors/blobs/Alchemy.java new file mode 100644 index 0000000000..fc02374af6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Alchemy.java @@ -0,0 +1,76 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.utils.Bundle; + +public class Alchemy extends Blob { + + protected int pos; + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + + for (int i=0; i < LENGTH; i++) { + if (cur[i] > 0) { + pos = i; + break; + } + } + } + + @Override + protected void evolve() { + volume = off[pos] = cur[pos]; + + if (Dungeon.visible[pos]) { + Journal.add( Journal.Feature.ALCHEMY ); + } + } + + @Override + public void seed( int cell, int amount ) { + cur[pos] = 0; + pos = cell; + volume = cur[pos] = amount; + } + + public static void transmute( int cell ) { + Heap heap = Dungeon.level.heaps.get( cell ); + if (heap != null) { + + Item result = heap.transmute(); + if (result != null) { + Dungeon.level.drop( result, cell ).sprite.drop( cell ); + } + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + emitter.start( Speck.factory( Speck.BUBBLE ), 0.4f, 0 ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Blob.java b/src/com/watabou/pixeldungeon/actors/blobs/Blob.java new file mode 100644 index 0000000000..f92db95370 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Blob.java @@ -0,0 +1,212 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import java.util.Arrays; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.utils.Bundle; + +public class Blob extends Actor { + + public static final int WIDTH = Level.WIDTH; + public static final int HEIGHT = Level.HEIGHT; + public static final int LENGTH = Level.LENGTH; + + public int volume = 0; + + public int[] cur; + protected int[] off; + + public BlobEmitter emitter; + + protected Blob() { + + cur = new int[LENGTH]; + off = new int[LENGTH]; + + volume = 0; + } + + private static final String CUR = "cur"; + private static final String START = "start"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + + if (volume > 0) { + + int start; + for (start=0; start < LENGTH; start++) { + if (cur[start] > 0) { + break; + } + } + int end; + for (end=LENGTH-1; end > start; end--) { + if (cur[end] > 0) { + break; + } + } + + bundle.put( START, start ); + bundle.put( CUR, trim( start, end + 1 ) ); + + } + } + + private int[] trim( int start, int end ) { + int len = end - start; + int[] copy = new int[len]; + System.arraycopy( cur, start, copy, 0, len ); + return copy; + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + + super.restoreFromBundle( bundle ); + + int[] data = bundle.getIntArray( CUR ); + if (data != null) { + int start = bundle.getInt( START ); + for (int i=0; i < data.length; i++) { + cur[i + start] = data[i]; + volume += data[i]; + } + } + + if (Level.resizingNeeded) { + int[] cur = new int[Level.LENGTH]; + Arrays.fill( cur, 0 ); + + int loadedMapSize = Level.loadedMapSize; + for (int i=0; i < loadedMapSize; i++) { + System.arraycopy( this.cur, i * loadedMapSize, cur, i * Level.WIDTH, loadedMapSize ); + } + + this.cur = cur; + } + } + + @Override + public boolean act() { + + spend( TICK ); + + if (volume > 0) { + + volume = 0; + evolve(); + + int[] tmp = off; + off = cur; + cur = tmp; + + } + + return true; + } + + public void use( BlobEmitter emitter ) { + this.emitter = emitter; + } + + protected void evolve() { + + boolean[] notBlocking = BArray.not( Level.solid, null ); + + for (int i=1; i < HEIGHT-1; i++) { + + int from = i * WIDTH + 1; + int to = from + WIDTH - 2; + + for (int pos=from; pos < to; pos++) { + if (notBlocking[pos]) { + + int count = 1; + int sum = cur[pos]; + + if (notBlocking[pos-1]) { + sum += cur[pos-1]; + count++; + } + if (notBlocking[pos+1]) { + sum += cur[pos+1]; + count++; + } + if (notBlocking[pos-WIDTH]) { + sum += cur[pos-WIDTH]; + count++; + } + if (notBlocking[pos+WIDTH]) { + sum += cur[pos+WIDTH]; + count++; + } + + int value = sum >= count ? (sum / count) - 1 : 0; + off[pos] = value; + + volume += value; + } else { + off[pos] = 0; + } + } + } + } + + public void seed( int cell, int amount ) { + cur[cell] += amount; + volume += amount; + } + + public void clear( int cell ) { + volume -= cur[cell]; + cur[cell] = 0; + } + + public String tileDesc() { + return null; + } + + @SuppressWarnings("unchecked") + public static T seed( int cell, int amount, Class type ) { + try { + + T gas = (T)Dungeon.level.blobs.get( type ); + if (gas == null) { + gas = type.newInstance(); + Dungeon.level.blobs.put( type, gas ); + } + + gas.seed( cell, amount ); + + return gas; + + } catch (Exception e) { + PixelDungeon.reportException( e ); + return null; + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Fire.java b/src/com/watabou/pixeldungeon/actors/blobs/Fire.java new file mode 100644 index 0000000000..79fe55a98e --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Fire.java @@ -0,0 +1,114 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class Fire extends Blob { + + @Override + protected void evolve() { + + boolean[] flamable = Level.flamable; + + int from = WIDTH + 1; + int to = Level.LENGTH - WIDTH - 1; + + boolean observe = false; + + for (int pos=from; pos < to; pos++) { + + int fire; + + if (cur[pos] > 0) { + + burn( pos ); + + fire = cur[pos] - 1; + if (fire <= 0 && flamable[pos]) { + + int oldTile = Dungeon.level.map[pos]; + Level.set( pos, Terrain.EMBERS ); + + observe = true; + GameScene.updateMap( pos ); + if (Dungeon.visible[pos]) { + GameScene.discoverTile( pos, oldTile ); + } + } + + } else { + + if (flamable[pos] && (cur[pos-1] > 0 || cur[pos+1] > 0 || cur[pos-WIDTH] > 0 || cur[pos+WIDTH] > 0)) { + fire = 4; + burn( pos ); + } else { + fire = 0; + } + + } + + volume += (off[pos] = fire); + + } + + if (observe) { + Dungeon.observe(); + } + } + + private void burn( int pos ) { + Char ch = Actor.findChar( pos ); + if (ch != null) { + Buff.affect( ch, Burning.class ).reignite( ch ); + } + + Heap heap = Dungeon.level.heaps.get( pos ); + if (heap != null) { + heap.burn(); + } + } + + public void seed( int cell, int amount ) { + if (cur[cell] == 0) { + volume += amount; + cur[cell] = amount; + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + emitter.start( FlameParticle.FACTORY, 0.03f, 0 ); + } + + @Override + public String tileDesc() { + return "A fire is raging here."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Foliage.java b/src/com/watabou/pixeldungeon/actors/blobs/Foliage.java new file mode 100644 index 0000000000..f6099c31a3 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Foliage.java @@ -0,0 +1,86 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Shadows; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.particles.ShaftParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class Foliage extends Blob { + + @Override + protected void evolve() { + + int from = WIDTH + 1; + int to = Level.LENGTH - WIDTH - 1; + + int[] map = Dungeon.level.map; + boolean regrowth = false; + + boolean visible = false; + + for (int pos=from; pos < to; pos++) { + if (cur[pos] > 0) { + + off[pos] = cur[pos]; + volume += off[pos]; + + if (map[pos] == Terrain.EMBERS) { + map[pos] = Terrain.GRASS; + regrowth = true; + } + + visible = visible || Dungeon.visible[pos]; + + } else { + off[pos] = 0; + } + } + + Hero hero = Dungeon.hero; + if (hero.isAlive() && hero.visibleEnemies() == 0 && cur[hero.pos] > 0) { + Buff.affect( hero, Shadows.class ).prolong(); + } + + if (regrowth) { + GameScene.updateMap(); + } + + if (visible) { + Journal.add( Journal.Feature.GARDEN ); + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + emitter.start( ShaftParticle.FACTORY, 0.9f, 0 ); + } + + @Override + public String tileDesc() { + return "Shafts of light pierce the gloom of the underground garden."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Freezing.java b/src/com/watabou/pixeldungeon/actors/blobs/Freezing.java new file mode 100644 index 0000000000..9ec2aeccbd --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Freezing.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Frost; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.SnowParticle; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.utils.Random; + +public class Freezing { + + // It's not really a blob... + + public static void affect( int cell, Fire fire ) { + + Char ch = Actor.findChar( cell ); + if (ch != null) { + Buff.prolong( ch, Frost.class, Frost.duration( ch ) * Random.Float( 1.0f, 1.5f ) ); + } + + if (fire != null) { + fire.clear( cell ); + } + + Heap heap = Dungeon.level.heaps.get( cell ); + if (heap != null) { + heap.freeze(); + } + + if (Dungeon.visible[cell]) { + CellEmitter.get( cell ).start( SnowParticle.FACTORY, 0.2f, 6 ); + } + + + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/ParalyticGas.java b/src/com/watabou/pixeldungeon/actors/blobs/ParalyticGas.java new file mode 100644 index 0000000000..cae7c22381 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/ParalyticGas.java @@ -0,0 +1,52 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.Speck; + +public class ParalyticGas extends Blob { + + @Override + protected void evolve() { + super.evolve(); + + Char ch; + for (int i=0; i < LENGTH; i++) { + if (cur[i] > 0 && (ch = Actor.findChar( i )) != null) { + Buff.prolong( ch, Paralysis.class, Paralysis.duration( ch ) ); + } + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + + emitter.pour( Speck.factory( Speck.PARALYSIS ), 0.6f ); + } + + @Override + public String tileDesc() { + return "A cloud of paralytic gas is swirling here."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Regrowth.java b/src/com/watabou/pixeldungeon/actors/blobs/Regrowth.java new file mode 100644 index 0000000000..794c0248ac --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Regrowth.java @@ -0,0 +1,76 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.particles.LeafParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class Regrowth extends Blob { + + @Override + protected void evolve() { + super.evolve(); + + if (volume > 0) { + + boolean mapUpdated = false; + + for (int i=0; i < LENGTH; i++) { + if (off[i] > 0) { + int c = Dungeon.level.map[i]; + if (c == Terrain.EMPTY || c == Terrain.EMBERS || c == Terrain.EMPTY_DECO) { + + Level.set( i, cur[i] > 9 ? Terrain.HIGH_GRASS : Terrain.GRASS ); + mapUpdated = true; + + } else if (c == Terrain.GRASS && cur[i] > 9) { + + Level.set( i, Terrain.HIGH_GRASS ); + mapUpdated = true; + + } + + Char ch = Actor.findChar( i ); + if (ch != null) { + Buff.prolong( ch, Roots.class, TICK ); + } + } + } + + if (mapUpdated) { + GameScene.updateMap(); + Dungeon.observe(); + } + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + + emitter.start( LeafParticle.LEVEL_SPECIFIC, 0.2f, 0 ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/ToxicGas.java b/src/com/watabou/pixeldungeon/actors/blobs/ToxicGas.java new file mode 100644 index 0000000000..2db7044227 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/ToxicGas.java @@ -0,0 +1,93 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; + +public class ToxicGas extends Blob implements Hero.Doom { + + @Override + protected void evolve() { + super.evolve(); + + int levelDamage = 5 + Dungeon.depth * 5; + + Char ch; + for (int i=0; i < LENGTH; i++) { + if (cur[i] > 0 && (ch = Actor.findChar( i )) != null) { + + int damage = (ch.HT + levelDamage) / 40; + if (damage < 1) { + damage = 1; + } + + ch.damage( damage, this ); + } + } + + Blob blob = Dungeon.level.blobs.get( ParalyticGas.class ); + if (blob != null) { + + int par[] = blob.cur; + + for (int i=0; i < LENGTH; i++) { + + int t = cur[i]; + int p = par[i]; + + if (p >= t) { + volume -= t; + cur[i] = 0; + } else { + blob.volume -= p; + par[i] = 0; + } + } + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + + emitter.pour( Speck.factory( Speck.TOXIC ), 0.6f ); + } + + @Override + public String tileDesc() { + return "A greenish cloud of toxic gas is swirling here."; + } + + @Override + public void onDeath() { + + Badges.validateDeathFromGas(); + + Dungeon.fail( Utils.format( ResultDescriptions.GAS, Dungeon.depth ) ); + GLog.n( "You died from a toxic gas.." ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/WaterOfAwareness.java b/src/com/watabou/pixeldungeon/actors/blobs/WaterOfAwareness.java new file mode 100644 index 0000000000..4a589a3ce3 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/WaterOfAwareness.java @@ -0,0 +1,108 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.Journal.Feature; +import com.watabou.pixeldungeon.actors.buffs.Awareness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.Identification; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.GLog; + +public class WaterOfAwareness extends WellWater { + + private static final String TXT_PROCCED = + "As you take a sip, you feel the knowledge pours into your mind. " + + "Now you know everything about your equipped items. Also you sense " + + "all items on the level and know all its secrets."; + + @Override + protected boolean affectHero( Hero hero ) { + + Sample.INSTANCE.play( Assets.SND_DRINK ); + emitter.parent.add( new Identification( DungeonTilemap.tileCenterToWorld( pos ) ) ); + + hero.belongings.observe(); + + for (int i=0; i < Level.LENGTH; i++) { + + int terr = Dungeon.level.map[i]; + if ((Terrain.flags[terr] & Terrain.SECRET) != 0) { + + Level.set( i, Terrain.discover( terr ) ); + GameScene.updateMap( i ); + + if (Dungeon.visible[i]) { + GameScene.discoverTile( i, terr ); + } + } + } + + Buff.affect( hero, Awareness.class, Awareness.DURATION ); + Dungeon.observe(); + + Dungeon.hero.interrupt(); + + GLog.p( TXT_PROCCED ); + + Journal.remove( Feature.WELL_OF_AWARENESS ); + + return true; + } + + @Override + protected Item affectItem( Item item ) { + if (item.isIdentified()) { + return null; + } else { + item.identify(); + Badges.validateItemLevelAquired( item ); + + emitter.parent.add( new Identification( DungeonTilemap.tileCenterToWorld( pos ) ) ); + + Journal.remove( Feature.WELL_OF_AWARENESS ); + + return item; + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + emitter.pour( Speck.factory( Speck.QUESTION ), 0.3f ); + } + + @Override + public String tileDesc() { + return + "Power of knowledge radiates from the water of this well. " + + "Take a sip from it to reveal all secrets of equipped items."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/WaterOfHealth.java b/src/com/watabou/pixeldungeon/actors/blobs/WaterOfHealth.java new file mode 100644 index 0000000000..0710151e02 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/WaterOfHealth.java @@ -0,0 +1,83 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.Journal.Feature; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ShaftParticle; +import com.watabou.pixeldungeon.items.DewVial; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.potions.PotionOfHealing; +import com.watabou.pixeldungeon.utils.GLog; + +public class WaterOfHealth extends WellWater { + + private static final String TXT_PROCCED = + "As you take a sip, you feel your wounds heal completely."; + + @Override + protected boolean affectHero( Hero hero ) { + + Sample.INSTANCE.play( Assets.SND_DRINK ); + + PotionOfHealing.heal( hero ); + hero.belongings.uncurseEquipped(); + ((Hunger)hero.buff( Hunger.class )).satisfy( Hunger.STARVING ); + + CellEmitter.get( pos ).start( ShaftParticle.FACTORY, 0.2f, 3 ); + + Dungeon.hero.interrupt(); + + GLog.p( TXT_PROCCED ); + + Journal.remove( Feature.WELL_OF_HEALTH ); + + return true; + } + + @Override + protected Item affectItem( Item item ) { + if (item instanceof DewVial && !((DewVial)item).isFull()) { + ((DewVial)item).fill(); + return item; + } + + return null; + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + emitter.start( Speck.factory( Speck.HEALING ), 0.5f, 0 ); + } + + @Override + public String tileDesc() { + return + "Power of health radiates from the water of this well. " + + "Take a sip from it to heal your wounds and satisfy hunger."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/WaterOfTransmutation.java b/src/com/watabou/pixeldungeon/actors/blobs/WaterOfTransmutation.java new file mode 100644 index 0000000000..07c82c348d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/WaterOfTransmutation.java @@ -0,0 +1,237 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.Journal.Feature; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Generator.Category; +import com.watabou.pixeldungeon.items.potions.Potion; +import com.watabou.pixeldungeon.items.potions.PotionOfMight; +import com.watabou.pixeldungeon.items.potions.PotionOfStrength; +import com.watabou.pixeldungeon.items.rings.Ring; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfWeaponUpgrade; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.items.weapon.Weapon.Enchantment; +import com.watabou.pixeldungeon.items.weapon.melee.*; +import com.watabou.pixeldungeon.plants.Plant; + +public class WaterOfTransmutation extends WellWater { + + @Override + protected Item affectItem( Item item ) { + + if (item instanceof MeleeWeapon) { + + return changeWeapon( (MeleeWeapon)item ); + + } else if (item instanceof Scroll) { + + Journal.remove( Feature.WELL_OF_TRANSMUTATION ); + return changeScroll( (Scroll)item ); + + } else if (item instanceof Potion) { + + Journal.remove( Feature.WELL_OF_TRANSMUTATION ); + return changePotion( (Potion)item ); + + } else if (item instanceof Ring) { + + Journal.remove( Feature.WELL_OF_TRANSMUTATION ); + return changeRing( (Ring)item ); + + } else if (item instanceof Wand) { + + Journal.remove( Feature.WELL_OF_TRANSMUTATION ); + return changeWand( (Wand)item ); + + } else if (item instanceof Plant.Seed) { + + Journal.remove( Feature.WELL_OF_TRANSMUTATION ); + return changeSeed( (Plant.Seed)item ); + + } else { + return null; + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + emitter.start( Speck.factory( Speck.CHANGE ), 0.2f, 0 ); + } + + private MeleeWeapon changeWeapon( MeleeWeapon w ) { + + MeleeWeapon n = null; + + if (w instanceof Knuckles) { + n = new Dagger(); + } else if (w instanceof Dagger) { + n = new Knuckles(); + } + + else if (w instanceof Spear) { + n = new Quarterstaff(); + } else if (w instanceof Quarterstaff) { + n = new Spear(); + } + + else if (w instanceof Sword) { + n = new Mace(); + } else if (w instanceof Mace) { + n = new Sword(); + } + + else if (w instanceof Longsword) { + n = new BattleAxe(); + } else if (w instanceof BattleAxe) { + n = new Longsword(); + } + + else if (w instanceof Glaive) { + n = new WarHammer(); + } else if (w instanceof WarHammer) { + n = new Glaive(); + } + + if (n != null) { + + int level = w.level; + if (level > 0) { + n.upgrade( level ); + } else if (level < 0) { + n.degrade( -level ); + } + + if (w.isEnchanted()) { + n.enchant( Enchantment.random() ); + } + + n.levelKnown = w.levelKnown; + n.cursedKnown = w.cursedKnown; + n.cursed = w.cursed; + + Journal.remove( Feature.WELL_OF_TRANSMUTATION ); + + return n; + } else { + return null; + } + } + + private Ring changeRing( Ring r ) { + Ring n; + do { + n = (Ring)Generator.random( Category.RING ); + } while (n.getClass() == r.getClass()); + + n.level = 0; + + int level = r.level; + if (level > 0) { + n.upgrade( level ); + } else if (level < 0) { + n.degrade( -level ); + } + + n.levelKnown = r.levelKnown; + n.cursedKnown = r.cursedKnown; + n.cursed = r.cursed; + + return n; + } + + private Wand changeWand( Wand w ) { + + Wand n; + do { + n = (Wand)Generator.random( Category.WAND ); + } while (n.getClass() == w.getClass()); + + n.level = 0; + n.upgrade( w.level ); + + n.levelKnown = w.levelKnown; + n.cursedKnown = w.cursedKnown; + n.cursed = w.cursed; + + return n; + } + + private Plant.Seed changeSeed( Plant.Seed s ) { + + Plant.Seed n; + + do { + n = (Plant.Seed)Generator.random( Category.SEED ); + } while (n.getClass() == s.getClass()); + + return n; + } + + private Scroll changeScroll( Scroll s ) { + if (s instanceof ScrollOfUpgrade) { + + return new ScrollOfWeaponUpgrade(); + + } else if (s instanceof ScrollOfWeaponUpgrade) { + + return new ScrollOfUpgrade(); + + } else { + + Scroll n; + do { + n = (Scroll)Generator.random( Category.SCROLL ); + } while (n.getClass() == s.getClass()); + return n; + } + } + + private Potion changePotion( Potion p ) { + if (p instanceof PotionOfStrength) { + + return new PotionOfMight(); + + } else if (p instanceof PotionOfMight) { + + return new PotionOfStrength(); + + } else { + + Potion n; + do { + n = (Potion)Generator.random( Category.POTION ); + } while (n.getClass() == p.getClass()); + return n; + } + } + + @Override + public String tileDesc() { + return + "Power of change radiates from the water of this well. " + + "Throw an item into the well to turn it into something else."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/Web.java b/src/com/watabou/pixeldungeon/actors/blobs/Web.java new file mode 100644 index 0000000000..00695bbf36 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/Web.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.particles.WebParticle; + +public class Web extends Blob { + + @Override + protected void evolve() { + + for (int i=0; i < LENGTH; i++) { + + int offv = cur[i] > 0 ? cur[i] - 1 : 0; + off[i] = offv; + + if (offv > 0) { + + volume += offv; + + Char ch = Actor.findChar( i ); + if (ch != null) { + Buff.prolong( ch, Roots.class, TICK ); + } + } + } + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + + emitter.pour( WebParticle.FACTORY, 0.4f ); + } + + public void seed( int cell, int amount ) { + int diff = amount - cur[cell]; + if (diff > 0) { + cur[cell] = amount; + volume += diff; + } + } + + @Override + public String tileDesc() { + return "Everything is covered with a thick web here."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/blobs/WellWater.java b/src/com/watabou/pixeldungeon/actors/blobs/WellWater.java new file mode 100644 index 0000000000..d59956d6a8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/blobs/WellWater.java @@ -0,0 +1,147 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.blobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.Journal.Feature; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class WellWater extends Blob { + + protected int pos; + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + + for (int i=0; i < LENGTH; i++) { + if (cur[i] > 0) { + pos = i; + break; + } + } + } + + @Override + protected void evolve() { + volume = off[pos] = cur[pos]; + + if (Dungeon.visible[pos]) { + if (this instanceof WaterOfAwareness) { + Journal.add( Feature.WELL_OF_AWARENESS ); + } else if (this instanceof WaterOfHealth) { + Journal.add( Feature.WELL_OF_HEALTH ); + } else if (this instanceof WaterOfTransmutation) { + Journal.add( Feature.WELL_OF_TRANSMUTATION ); + } + } + } + + protected boolean affect() { + + Heap heap; + + if (pos == Dungeon.hero.pos && affectHero( Dungeon.hero )) { + + volume = off[pos] = cur[pos] = 0; + return true; + + } else if ((heap = Dungeon.level.heaps.get( pos )) != null) { + + Item oldItem = heap.peek(); + Item newItem = affectItem( oldItem ); + + if (newItem != null) { + + if (newItem == oldItem) { + + } else if (oldItem.quantity() > 1) { + + oldItem.quantity( oldItem.quantity() - 1 ); + heap.drop( newItem ); + + } else { + heap.replace( oldItem, newItem ); + } + + heap.sprite.link(); + volume = off[pos] = cur[pos] = 0; + + return true; + + } else { + + int newPlace; + do { + newPlace = pos + Level.NEIGHBOURS8[Random.Int( 8 )]; + } while (!Level.passable[newPlace] && !Level.avoid[newPlace]); + Dungeon.level.drop( heap.pickUp(), newPlace ).sprite.drop( pos ); + + return false; + + } + + } else { + + return false; + + } + } + + protected boolean affectHero( Hero hero ) { + return false; + } + + protected Item affectItem( Item item ) { + return null; + } + + @Override + public void seed( int cell, int amount ) { + cur[pos] = 0; + pos = cell; + volume = cur[pos] = amount; + } + + public static void affectCell( int cell ) { + + Class[] waters = {WaterOfHealth.class, WaterOfAwareness.class, WaterOfTransmutation.class}; + + for (ClasswaterClass : waters) { + WellWater water = (WellWater)Dungeon.level.blobs.get( waterClass ); + if (water != null && + water.volume > 0 && + water.pos == cell && + water.affect()) { + + Level.set( cell, Terrain.EMPTY_WELL ); + GameScene.updateMap( cell ); + + return; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Amok.java b/src/com/watabou/pixeldungeon/actors/buffs/Amok.java new file mode 100644 index 0000000000..48a260a46e --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Amok.java @@ -0,0 +1,33 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Amok extends FlavourBuff { + + @Override + public int icon() { + return BuffIndicator.AMOK; + } + + @Override + public String toString() { + return "Amok"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Awareness.java b/src/com/watabou/pixeldungeon/actors/buffs/Awareness.java new file mode 100644 index 0000000000..252e901e6f --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Awareness.java @@ -0,0 +1,31 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; + +public class Awareness extends FlavourBuff { + + public static final float DURATION = 2f; + + @Override + public void detach() { + super.detach(); + Dungeon.observe(); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Barkskin.java b/src/com/watabou/pixeldungeon/actors/buffs/Barkskin.java new file mode 100644 index 0000000000..e8e53ae8ee --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Barkskin.java @@ -0,0 +1,63 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Barkskin extends Buff { + + private int level = 0; + + @Override + public boolean act() { + if (target.isAlive()) { + + spend( TICK ); + if (--level <= 0) { + detach(); + } + + } else { + + detach(); + + } + + return true; + } + + public int level() { + return level; + } + + public void level( int value ) { + if (level < value) { + level = value; + } + } + + @Override + public int icon() { + return BuffIndicator.BARKSKIN; + } + + @Override + public String toString() { + return "Barkskin"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Bleeding.java b/src/com/watabou/pixeldungeon/actors/buffs/Bleeding.java new file mode 100644 index 0000000000..32f8d9f07d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Bleeding.java @@ -0,0 +1,93 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.effects.Splash; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class Bleeding extends Buff { + + protected int level; + + private static final String LEVEL = "level"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( LEVEL, level ); + + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + level = bundle.getInt( LEVEL ); + } + + public void set( int level ) { + this.level = level; + }; + + @Override + public int icon() { + return BuffIndicator.BLEEDING; + } + + @Override + public String toString() { + return "Bleeding"; + } + + @Override + public boolean act() { + if (target.isAlive()) { + + if ((level = Random.Int( level / 2, level )) > 0) { + + target.damage( level, this ); + if (target.sprite.visible) { + Splash.at( target.sprite.center(), -PointF.PI / 2, PointF.PI / 6, + target.sprite.blood(), Math.min( 10 * level / target.HT, 10 ) ); + } + + if (target == Dungeon.hero && !target.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.BLEEDING, Dungeon.depth ) ); + GLog.n( "You bled to death..." ); + } + + spend( TICK ); + } else { + detach(); + } + + } else { + + detach(); + + } + + return true; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Blindness.java b/src/com/watabou/pixeldungeon/actors/buffs/Blindness.java new file mode 100644 index 0000000000..4ef36a9ff0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Blindness.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Blindness extends FlavourBuff { + + @Override + public void detach() { + super.detach(); + Dungeon.observe(); + } + + @Override + public int icon() { + return BuffIndicator.BLINDNESS; + } + + @Override + public String toString() { + return "Blinded"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Buff.java b/src/com/watabou/pixeldungeon/actors/buffs/Buff.java new file mode 100644 index 0000000000..0b5aa1632e --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Buff.java @@ -0,0 +1,90 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Buff extends Actor { + + public Char target; + + public boolean attachTo( Char target ) { + + if (target.immunities().contains( getClass() )) { + return false; + } + + this.target = target; + target.add( this ); + + return true; + } + + public void detach() { + target.remove( this ); + } + + @Override + public boolean act() { + diactivate(); + return true; + } + + public int icon() { + return BuffIndicator.NONE; + } + + public static T affect( Char target, Class buffClass ) { + T buff = target.buff( buffClass ); + if (buff != null) { + return buff; + } else { + try { + buff = buffClass.newInstance(); + buff.attachTo( target ); + return buff; + } catch (Exception e) { + return null; + } + } + } + + public static T affect( Char target, Class buffClass, float duration ) { + T buff = affect( target, buffClass ); + buff.spend( duration ); + return buff; + } + + public static T prolong( Char target, Class buffClass, float duration ) { + T buff = affect( target, buffClass ); + buff.postpone( duration ); + return buff; + } + + public static void detach( Buff buff ) { + if (buff != null) { + buff.detach(); + } + } + + public static void detach( Char target, Class cl ) { + detach( target.buff( cl ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Burning.java b/src/com/watabou/pixeldungeon/actors/buffs/Burning.java new file mode 100644 index 0000000000..e88687b2bd --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Burning.java @@ -0,0 +1,154 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Thief; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.food.ChargrilledMeat; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Burning extends Buff implements Hero.Doom { + + private static final String TXT_BURNS_UP = "%s burns up!"; + private static final String TXT_BURNED_TO_DEATH = "You burned to death..."; + + private static final float DURATION = 8f; + + private float left; + + private static final String LEFT = "left"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( LEFT, left ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle(bundle); + left = bundle.getFloat( LEFT ); + } + + @Override + public boolean act() { + + if (target.isAlive()) { + + if (target instanceof Hero) { + Buff.prolong( target, Light.class, TICK * 1.01f ); + } + + target.damage( Random.Int( 1, 5 ), this ); + + if (target instanceof Hero) { + + Item item = ((Hero)target).belongings.randomUnequipped(); + if (item instanceof Scroll) { + + item = item.detach( ((Hero)target).belongings.backpack ); + GLog.w( TXT_BURNS_UP, item.toString() ); + + Heap.burnFX( target.pos ); + + } else if (item instanceof MysteryMeat) { + + item = item.detach( ((Hero)target).belongings.backpack ); + ChargrilledMeat steak = new ChargrilledMeat(); + if (!steak.collect( ((Hero)target).belongings.backpack )) { + Dungeon.level.drop( steak, target.pos ).sprite.drop(); + } + GLog.w( TXT_BURNS_UP, item.toString() ); + + Heap.burnFX( target.pos ); + + } + + } else if (target instanceof Thief && ((Thief)target).item instanceof Scroll) { + + ((Thief)target).item = null; + target.sprite.emitter().burst( ElmoParticle.FACTORY, 6 ); + } + + } else { + detach(); + } + + if (Level.flamable[target.pos]) { + GameScene.add( Blob.seed( target.pos, 4, Fire.class ) ); + } + + spend( TICK ); + left -= TICK; + + if (left <= 0 || + Random.Float() > (2 + (float)target.HP / target.HT) / 3 || + (Level.water[target.pos] && !target.flying)) { + + detach(); + } + + return true; + } + + public void reignite( Char ch ) { + left = duration( ch ); + } + + @Override + public int icon() { + return BuffIndicator.FIRE; + } + + @Override + public String toString() { + return "Burning"; + } + + public static float duration( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() * DURATION : DURATION; + } + + @Override + public void onDeath() { + + Badges.validateDeathFromFire(); + + Dungeon.fail( Utils.format( ResultDescriptions.BURNING, Dungeon.depth ) ); + GLog.n( TXT_BURNED_TO_DEATH ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Charm.java b/src/com/watabou/pixeldungeon/actors/buffs/Charm.java new file mode 100644 index 0000000000..d363accfc1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Charm.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Charm extends FlavourBuff { + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + target.pacified = true; + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.pacified = false; + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.HEART; + } + + @Override + public String toString() { + return "Charmed"; + } + + public static float durationFactor( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() : 1; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Combo.java b/src/com/watabou/pixeldungeon/actors/buffs/Combo.java new file mode 100644 index 0000000000..9956f47c85 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Combo.java @@ -0,0 +1,67 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; + +public class Combo extends Buff { + + private static String TXT_COMBO = "%d hit combo!"; + + public int count = 0; + + @Override + public int icon() { + return BuffIndicator.COMBO; + } + + @Override + public String toString() { + return "Combo"; + } + + public int hit( Char enemy, int damage ) { + + count++; + + if (count >= 3) { + + Badges.validateMasteryCombo( count ); + + GLog.p( TXT_COMBO, count ); + postpone( 1.41f - count / 10f ); + return (int)(damage * (count - 2) / 5f); + + } else { + + postpone( 1.1f ); + return 0; + + } + } + + @Override + public boolean act() { + detach(); + return true; + } + +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Cripple.java b/src/com/watabou/pixeldungeon/actors/buffs/Cripple.java new file mode 100644 index 0000000000..c50c3b418d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Cripple.java @@ -0,0 +1,35 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Cripple extends FlavourBuff { + + public static final float DURATION = 10f; + + @Override + public int icon() { + return BuffIndicator.CRIPPLE; + } + + @Override + public String toString() { + return "Crippled"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/FlavourBuff.java b/src/com/watabou/pixeldungeon/actors/buffs/FlavourBuff.java new file mode 100644 index 0000000000..01cb7ffe59 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/FlavourBuff.java @@ -0,0 +1,28 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +// Special kind of buff, that doesn't perform any kind actions +public class FlavourBuff extends Buff { + + @Override + public boolean act() { + detach(); + return true; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Frost.java b/src/com/watabou/pixeldungeon/actors/buffs/Frost.java new file mode 100644 index 0000000000..18546ce811 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Frost.java @@ -0,0 +1,79 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.food.FrozenCarpaccio; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Frost extends FlavourBuff { + + private static final float DURATION = 5f; + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + + target.paralysed = true; + Burning.detach( target, Burning.class ); + + if (target instanceof Hero) { + Hero hero = (Hero)target; + Item item = hero.belongings.randomUnequipped(); + if (item instanceof MysteryMeat) { + + item = item.detach( hero.belongings.backpack ); + FrozenCarpaccio carpaccio = new FrozenCarpaccio(); + if (!carpaccio.collect( hero.belongings.backpack )) { + Dungeon.level.drop( carpaccio, target.pos ).sprite.drop(); + } + } + } + + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.paralysed = false; + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.FROST; + } + + @Override + public String toString() { + return "Frozen"; + } + + public static float duration( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() * DURATION : DURATION; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Fury.java b/src/com/watabou/pixeldungeon/actors/buffs/Fury.java new file mode 100644 index 0000000000..fb2777a471 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Fury.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Fury extends Buff { + + public static float LEVEL = 0.4f; + + @Override + public boolean act() { + if (target.HP > target.HT * LEVEL) { + detach(); + } + + spend( TICK ); + + return true; + } + + @Override + public int icon() { + return BuffIndicator.FURY; + } + + @Override + public String toString() { + return "Fury"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/GasesImmunity.java b/src/com/watabou/pixeldungeon/actors/buffs/GasesImmunity.java new file mode 100644 index 0000000000..fab1d89fb9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/GasesImmunity.java @@ -0,0 +1,44 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class GasesImmunity extends FlavourBuff { + + public static final float DURATION = 5f; + + @Override + public int icon() { + return BuffIndicator.IMMUNITY; + } + + @Override + public String toString() { + return "Immune to gases"; + } + + public static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Paralysis.class ); + IMMUNITIES.add( ToxicGas.class ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Hunger.java b/src/com/watabou/pixeldungeon/actors/buffs/Hunger.java new file mode 100644 index 0000000000..4e4ea63c88 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Hunger.java @@ -0,0 +1,159 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.items.rings.RingOfSatiety; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Hunger extends Buff implements Hero.Doom { + + private static final float STEP = 10f; + + public static final float HUNGRY = 260f; + public static final float STARVING = 360f; + + private static final String TXT_HUNGRY = "You are hungry."; + private static final String TXT_STARVING = "You are starving!"; + private static final String TXT_DEATH = "You starved to death..."; + + private float level; + + private static final String LEVEL = "level"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( LEVEL, level ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + level = bundle.getFloat( LEVEL ); + } + + @Override + public boolean act() { + if (target.isAlive()) { + + Hero hero = (Hero)target; + + if (isStarving()) { + + if (Random.Float() < 0.3f && (target.HP > 1 || !target.paralysed)) { + + GLog.n( TXT_STARVING ); + hero.damage( 1, this ); + + hero.interrupt(); + } + } else { + + int bonus = 0; + for (Buff buff : target.buffs( RingOfSatiety.Satiety.class )) { + bonus += ((RingOfSatiety.Satiety)buff).level; + } + + float newLevel = level + STEP - bonus; + boolean statusUpdated = false; + if (newLevel >= STARVING) { + + GLog.n( TXT_STARVING ); + statusUpdated = true; + + hero.interrupt(); + + } else if (newLevel >= HUNGRY && level < HUNGRY) { + + GLog.w( TXT_HUNGRY ); + statusUpdated = true; + + } + level = newLevel; + + if (statusUpdated) { + BuffIndicator.refreshHero(); + } + + } + + float step = ((Hero)target).heroClass == HeroClass.ROGUE ? STEP * 1.2f : STEP; + spend( target.buff( Shadows.class ) == null ? step : step * 1.5f ); + + } else { + + diactivate(); + + } + + return true; + } + + public void satisfy( float energy ) { + level -= energy; + if (level < 0) { + level = 0; + } else if (level > STARVING) { + level = STARVING; + } + + BuffIndicator.refreshHero(); + } + + public boolean isStarving() { + return level >= STARVING; + } + + @Override + public int icon() { + if (level < HUNGRY) { + return BuffIndicator.NONE; + } else if (level < STARVING) { + return BuffIndicator.HUNGER; + } else { + return BuffIndicator.STARVATION; + } + } + + @Override + public String toString() { + if (level < STARVING) { + return "Hungry"; + } else { + return "Starving"; + } + } + + @Override + public void onDeath() { + + Badges.validateDeathFromHunger(); + + Dungeon.fail( Utils.format( ResultDescriptions.HUNGER, Dungeon.depth ) ); + GLog.n( TXT_DEATH ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Invisibility.java b/src/com/watabou/pixeldungeon/actors/buffs/Invisibility.java new file mode 100644 index 0000000000..cf44544e3c --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Invisibility.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Invisibility extends FlavourBuff { + + public static final float DURATION = 15f; + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + target.invisible++; + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.invisible--; + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.INVISIBLE; + } + + @Override + public String toString() { + return "Invisible"; + } + + public static void dispel() { + Invisibility buff = Dungeon.hero.buff( Invisibility.class ); + if (buff != null && Dungeon.hero.visibleEnemies() > 0) { + buff.detach(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Levitation.java b/src/com/watabou/pixeldungeon/actors/buffs/Levitation.java new file mode 100644 index 0000000000..c3d64c850e --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Levitation.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Levitation extends FlavourBuff { + + public static final float DURATION = 20f; + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + target.flying = true; + Roots.detach( target, Roots.class ); + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.flying = false; + Dungeon.level.press( target.pos, target ); + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.LEVITATION; + } + + @Override + public String toString() { + return "Levitating"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Light.java b/src/com/watabou/pixeldungeon/actors/buffs/Light.java new file mode 100644 index 0000000000..7f2baa4381 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Light.java @@ -0,0 +1,59 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Light extends FlavourBuff { + + public static final float DURATION = 250f; + public static final int DISTANCE = 4; + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + // When a level is loading, do nothing + if (Dungeon.level != null) { + target.viewDistance = Math.max( Dungeon.level.viewDistance, DISTANCE ); + Dungeon.observe(); + } + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.viewDistance = Dungeon.level.viewDistance; + Dungeon.observe(); + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.LIGHT; + } + + @Override + public String toString() { + return "Illuminated"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/MindVision.java b/src/com/watabou/pixeldungeon/actors/buffs/MindVision.java new file mode 100644 index 0000000000..4032e60ef6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/MindVision.java @@ -0,0 +1,44 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class MindVision extends FlavourBuff { + + public static final float DURATION = 20f; + + public int distance = 2; + + @Override + public int icon() { + return BuffIndicator.MIND_VISION; + } + + @Override + public String toString() { + return "Mind vision"; + } + + @Override + public void detach() { + super.detach(); + Dungeon.observe(); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Ooze.java b/src/com/watabou/pixeldungeon/actors/buffs/Ooze.java new file mode 100644 index 0000000000..564e46d4dd --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Ooze.java @@ -0,0 +1,58 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; + +public class Ooze extends Buff { + + private static final String TXT_HERO_KILLED = "%s killed you..."; + + public int damage = 1; + + @Override + public int icon() { + return BuffIndicator.OOZE; + } + + @Override + public String toString() { + return "Caustic ooze"; + } + + @Override + public boolean act() { + if (target.isAlive()) { + target.damage( damage, this ); + if (!target.isAlive() && target == Dungeon.hero) { + Dungeon.fail( Utils.format( ResultDescriptions.OOZE, Dungeon.depth ) ); + GLog.n( TXT_HERO_KILLED, toString() ); + } + spend( TICK ); + } + if (Level.water[target.pos]) { + detach(); + } + return true; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Paralysis.java b/src/com/watabou/pixeldungeon/actors/buffs/Paralysis.java new file mode 100644 index 0000000000..4623951157 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Paralysis.java @@ -0,0 +1,58 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Paralysis extends FlavourBuff { + + private static final float DURATION = 10f; + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + target.paralysed = true; + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.paralysed = false; + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.PARALYSIS; + } + + @Override + public String toString() { + return "Paralysed"; + } + + public static float duration( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() * DURATION : DURATION; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Poison.java b/src/com/watabou/pixeldungeon/actors/buffs/Poison.java new file mode 100644 index 0000000000..fe223d5395 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Poison.java @@ -0,0 +1,98 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; + +public class Poison extends Buff implements Hero.Doom { + + public static final int DOT = 2; + + protected float left; + + private static final String LEFT = "left"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( LEFT, left ); + + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + left = bundle.getFloat( LEFT ); + } + + public void set( float duration ) { + this.left = duration; + }; + + @Override + public int icon() { + return BuffIndicator.POISON; + } + + @Override + public String toString() { + return "Poisoned"; + } + + @Override + public boolean act() { + if (target.isAlive()) { + + target.damage( DOT, this ); + spend( TICK ); + + if ((left -= TICK) <= 0) { + detach(); + } + + } else { + + detach(); + + } + + return true; + } + + public static float durationFactor( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() : 1; + } + + @Override + public void onDeath() { + Badges.validateDeathFromPoison(); + + Dungeon.fail( Utils.format( ResultDescriptions.POISON, Dungeon.depth ) ); + GLog.n( "You died from poison..." ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Regeneration.java b/src/com/watabou/pixeldungeon/actors/buffs/Regeneration.java new file mode 100644 index 0000000000..790dbdc7f5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Regeneration.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.rings.RingOfMending; + +public class Regeneration extends Buff { + + private static final float REGENERATION_DELAY = 10; + + @Override + public boolean act() { + if (target.isAlive()) { + + if (target.HP < target.HT && !((Hero)target).isStarving()) { + target.HP += 1; + } + + int bonus = 0; + for (Buff buff : target.buffs( RingOfMending.Rejuvenation.class )) { + bonus += ((RingOfMending.Rejuvenation)buff).level; + } + + spend( (float)(REGENERATION_DELAY / Math.pow( 1.2, bonus )) ); + + } else { + + diactivate(); + + } + + return true; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Roots.java b/src/com/watabou/pixeldungeon/actors/buffs/Roots.java new file mode 100644 index 0000000000..932dfbbcea --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Roots.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Roots extends FlavourBuff { + + @Override + public boolean attachTo( Char target ) { + if (!target.flying && super.attachTo( target )) { + target.rooted = true; + return true; + } else { + return false; + } + } + + @Override + public void detach() { + target.rooted = false; + super.detach(); + } + + @Override + public int icon() { + return BuffIndicator.ROOTS; + } + + @Override + public String toString() { + return "Rooted"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Shadows.java b/src/com/watabou/pixeldungeon/actors/buffs/Shadows.java new file mode 100644 index 0000000000..ae6e4fc928 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Shadows.java @@ -0,0 +1,96 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.utils.Bundle; + +public class Shadows extends Invisibility { + + protected float left; + + private static final String LEFT = "left"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( LEFT, left ); + + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + left = bundle.getFloat( LEFT ); + } + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + Sample.INSTANCE.play( Assets.SND_MELD ); + Dungeon.observe(); + return true; + } else { + return false; + } + } + + @Override + public void detach() { + super.detach(); + Dungeon.observe(); + } + + @Override + public boolean act() { + if (target.isAlive()) { + + spend( TICK * 2 ); + + if (--left <= 0 || Dungeon.hero.visibleEnemies() > 0) { + detach(); + } + + } else { + + detach(); + + } + + return true; + } + + public void prolong() { + left = 2; + } + + @Override + public int icon() { + return BuffIndicator.SHADOWS; + } + + @Override + public String toString() { + return "Shadowmelded"; + } + +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Sleep.java b/src/com/watabou/pixeldungeon/actors/buffs/Sleep.java new file mode 100644 index 0000000000..7ffff90e13 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Sleep.java @@ -0,0 +1,24 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +public class Sleep extends FlavourBuff { + + public static final float SWS = 1.5f; + +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Slow.java b/src/com/watabou/pixeldungeon/actors/buffs/Slow.java new file mode 100644 index 0000000000..34f55234fb --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Slow.java @@ -0,0 +1,42 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Slow extends FlavourBuff { + + private static final float DURATION = 10f; + + @Override + public int icon() { + return BuffIndicator.SLOW; + } + + @Override + public String toString() { + return "Slowed"; + } + + public static float duration( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() * DURATION : DURATION; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/SnipersMark.java b/src/com/watabou/pixeldungeon/actors/buffs/SnipersMark.java new file mode 100644 index 0000000000..01ca64f758 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/SnipersMark.java @@ -0,0 +1,33 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class SnipersMark extends FlavourBuff { + + @Override + public int icon() { + return BuffIndicator.MARK; + } + + @Override + public String toString() { + return "Sniper's mark"; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Speed.java b/src/com/watabou/pixeldungeon/actors/buffs/Speed.java new file mode 100644 index 0000000000..cccdc6950c --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Speed.java @@ -0,0 +1,24 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +public class Speed extends FlavourBuff { + + public static final float DURATION = 10f; + +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Terror.java b/src/com/watabou/pixeldungeon/actors/buffs/Terror.java new file mode 100644 index 0000000000..d905e2d81d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Terror.java @@ -0,0 +1,53 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.utils.Bundle; + +public class Terror extends FlavourBuff { + + public static final float DURATION = 10f; + public Char source; + + @Override + public int icon() { + return BuffIndicator.TERROR; + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + // It's not really correct... + source = Dungeon.hero; + } + + @Override + public String toString() { + return "Terror"; + } + + public static void recover( Char target ) { + Terror terror = target.buff( Terror.class ); + if (terror != null && terror.cooldown() < DURATION) { + target.remove( terror ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/buffs/Weakness.java b/src/com/watabou/pixeldungeon/actors/buffs/Weakness.java new file mode 100644 index 0000000000..52df8f84b8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/buffs/Weakness.java @@ -0,0 +1,62 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.buffs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.rings.RingOfElements.Resistance; +import com.watabou.pixeldungeon.ui.BuffIndicator; + +public class Weakness extends FlavourBuff { + + private static final float DURATION = 40f; + + @Override + public int icon() { + return BuffIndicator.WEAKNESS; + } + + @Override + public String toString() { + return "Weakened"; + } + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + Hero hero = (Hero)target; + hero.weakened = true; + hero.belongings.discharge(); + + return true; + } else { + return false; + } + } + + @Override + public void detach() { + super.detach(); + ((Hero)target).weakened = false; + } + + public static float duration( Char ch ) { + Resistance r = ch.buff( Resistance.class ); + return r != null ? r.durationFactor() * DURATION : DURATION; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/hero/Belongings.java b/src/com/watabou/pixeldungeon/actors/hero/Belongings.java new file mode 100644 index 0000000000..88a594b338 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/hero/Belongings.java @@ -0,0 +1,299 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.hero; + +import java.util.Iterator; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.KindOfWeapon; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.items.keys.Key; +import com.watabou.pixeldungeon.items.rings.Ring; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfRemoveCurse; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Belongings implements Iterable { + + public static final int BACKPACK_SIZE = 19; + + private Hero owner; + + public Bag backpack; + + public KindOfWeapon weapon = null; + public Armor armor = null; + public Ring ring1 = null; + public Ring ring2 = null; + + public Belongings( Hero owner ) { + this.owner = owner; + + backpack = new Bag() {{ + name = "backpack"; + size = BACKPACK_SIZE; + }}; + backpack.owner = owner; + } + + private static final String WEAPON = "weapon"; + private static final String ARMOR = "armor"; + private static final String RING1 = "ring1"; + private static final String RING2 = "ring2"; + + public void storeInBundle( Bundle bundle ) { + + backpack.storeInBundle( bundle ); + + bundle.put( WEAPON, weapon ); + bundle.put( ARMOR, armor ); + bundle.put( RING1, ring1 ); + bundle.put( RING2, ring2 ); + } + + public void restoreFromBundle( Bundle bundle ) { + + backpack.clear(); + backpack.restoreFromBundle( bundle ); + + weapon = (KindOfWeapon)bundle.get( WEAPON ); + if (weapon != null) { + weapon.activate( owner ); + } + + armor = (Armor)bundle.get( ARMOR ); + + ring1 = (Ring)bundle.get( RING1 ); + if (ring1 != null) { + ring1.activate( owner ); + } + + ring2 = (Ring)bundle.get( RING2 ); + if (ring2 != null) { + ring2.activate( owner ); + } + } + + @SuppressWarnings("unchecked") + public T getItem( Class itemClass ) { + + for (Item item : this) { + if (itemClass.isInstance( item )) { + return (T)item; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public T getKey( Class kind, int depth ) { + + for (Item item : backpack) { + if (item.getClass() == kind && ((Key)item).depth == depth) { + return (T)item; + } + } + + return null; + } + + public void countIronKeys() { + + IronKey.curDepthQunatity = 0; + + for (Item item : backpack) { + if (item instanceof IronKey && ((IronKey)item).depth == Dungeon.depth) { + IronKey.curDepthQunatity = item.quantity(); + return; + } + } + } + + public void identify() { + for (Item item : this) { + item.identify(); + } + } + + public void observe() { + if (weapon != null) { + weapon.identify(); + Badges.validateItemLevelAquired( weapon ); + } + if (armor != null) { + armor.identify(); + Badges.validateItemLevelAquired( armor ); + } + if (ring1 != null) { + ring1.identify(); + Badges.validateItemLevelAquired( ring1 ); + } + if (ring2 != null) { + ring2.identify(); + Badges.validateItemLevelAquired( ring2 ); + } + for (Item item : backpack) { + item.cursedKnown = true; + } + } + + public void uncurseEquipped() { + ScrollOfRemoveCurse.uncurse( owner, armor, weapon, ring1, ring2 ); + } + + public Item randomUnequipped() { + return Random.element( backpack.items ); + } + + public void resurrect( int depth ) { + + for (Item item : backpack.items.toArray( new Item[0])) { + if (item instanceof Key) { + if (((Key)item).depth == depth) { + item.detachAll( backpack ); + } + } else if (item.unique) { + + } else if (!item.isEquipped( owner )) { + item.detachAll( backpack ); + } + } + + if (weapon != null) { + weapon.cursed = false; + weapon.activate( owner ); + } + + if (armor != null) { + armor.cursed = false; + } + + if (ring1 != null) { + ring1.cursed = false; + ring1.activate( owner ); + } + if (ring2 != null) { + ring2.cursed = false; + ring2.activate( owner ); + } + } + + public int charge( boolean full) { + + int count = 0; + + for (Item item : this) { + if (item instanceof Wand) { + Wand wand = (Wand)item; + if (wand.curCharges < wand.maxCharges) { + wand.curCharges = full ? wand.maxCharges : wand.curCharges + 1; + count++; + + wand.updateQuickslot(); + } + } + } + + return count; + } + + public int discharge() { + + int count = 0; + + for (Item item : this) { + if (item instanceof Wand) { + Wand wand = (Wand)item; + if (wand.curCharges > 0) { + wand.curCharges--; + count++; + + wand.updateQuickslot(); + } + } + } + + return count; + } + + @Override + public Iterator iterator() { + return new ItemIterator(); + } + + private class ItemIterator implements Iterator { + + private int index = 0; + + private Iterator backpackIterator = backpack.iterator(); + + private Item[] equipped = {weapon, armor, ring1, ring2}; + private int backpackIndex = equipped.length; + + @Override + public boolean hasNext() { + + for (int i=index; i < backpackIndex; i++) { + if (equipped[i] != null) { + return true; + } + } + + return backpackIterator.hasNext(); + } + + @Override + public Item next() { + + while (index < backpackIndex) { + Item item = equipped[index++]; + if (item != null) { + return item; + } + } + + return backpackIterator.next(); + } + + @Override + public void remove() { + switch (index) { + case 0: + equipped[0] = weapon = null; + break; + case 1: + equipped[1] = armor = null; + break; + case 2: + equipped[2] = ring1 = null; + break; + case 3: + equipped[3] = ring2 = null; + break; + default: + backpackIterator.remove(); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/hero/Hero.java b/src/com/watabou/pixeldungeon/actors/hero/Hero.java new file mode 100644 index 0000000000..daf8d57404 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/hero/Hero.java @@ -0,0 +1,1334 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.hero; + +import java.util.ArrayList; +import java.util.HashSet; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.GamesInProgress; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Barkskin; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Combo; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.actors.buffs.Fury; +import com.watabou.pixeldungeon.actors.buffs.GasesImmunity; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.buffs.Ooze; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Regeneration; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.buffs.Charm; +import com.watabou.pixeldungeon.actors.buffs.SnipersMark; +import com.watabou.pixeldungeon.actors.buffs.Weakness; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CheckedCell; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Amulet; +import com.watabou.pixeldungeon.items.Ankh; +import com.watabou.pixeldungeon.items.DewVial; +import com.watabou.pixeldungeon.items.Dewdrop; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Heap.Type; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.KindOfWeapon; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.keys.GoldenKey; +import com.watabou.pixeldungeon.items.keys.Key; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.items.potions.PotionOfStrength; +import com.watabou.pixeldungeon.items.rings.RingOfAccuracy; +import com.watabou.pixeldungeon.items.rings.RingOfDetection; +import com.watabou.pixeldungeon.items.rings.RingOfElements; +import com.watabou.pixeldungeon.items.rings.RingOfEvasion; +import com.watabou.pixeldungeon.items.rings.RingOfHaste; +import com.watabou.pixeldungeon.items.rings.RingOfShadows; +import com.watabou.pixeldungeon.items.rings.RingOfThorns; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfMagicMapping; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfRecharging; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.melee.MeleeWeapon; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.levels.features.AlchemyPot; +import com.watabou.pixeldungeon.levels.features.Chasm; +import com.watabou.pixeldungeon.plants.Earthroot; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.InterlevelScene; +import com.watabou.pixeldungeon.scenes.SurfaceScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.HeroSprite; +import com.watabou.pixeldungeon.ui.AttackIndicator; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.ui.QuickSlot; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndMessage; +import com.watabou.pixeldungeon.windows.WndResurrect; +import com.watabou.pixeldungeon.windows.WndTradeItem; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Hero extends Char { + + private static final String TXT_LEAVE = "One does not simply leave Pixel Dungeon."; + + private static final String TXT_LEVEL_UP = "level up!"; + private static final String TXT_NEW_LEVEL = + "Welcome to level %d! Now you are healthier and more focused. " + + "It's easier for you to hit enemies and dodge their attacks."; + + public static final String TXT_YOU_NOW_HAVE = "You now have %s"; + + private static final String TXT_SOMETHING_ELSE = "There is something else here"; + private static final String TXT_LOCKED_CHEST = "This chest is locked and you don't have matching key"; + private static final String TXT_LOCKED_DOOR = "You don't have a matching key"; + private static final String TXT_NOTICED_SMTH = "You noticed something"; + + private static final String TXT_WAIT = "..."; + private static final String TXT_SEARCH = "search"; + + public static final int STARTING_STR = 10; + + private static final float TIME_TO_REST = 1f; + private static final float TIME_TO_SEARCH = 2f; + + public HeroClass heroClass = HeroClass.ROGUE; + public HeroSubClass subClass = HeroSubClass.NONE; + + private int attackSkill = 10; + private int defenseSkill = 5; + + public boolean ready = false; + public HeroAction curAction = null; + public HeroAction lastAction = null; + + private Char enemy; + + public Armor.Glyph killerGlyph = null; + + private Item theKey; + + public boolean restoreHealth = false; + + public boolean usingRanged = false; + public Belongings belongings; + + public int STR; + public boolean weakened = false; + + public float awareness; + + public int lvl = 1; + public int exp = 0; + + private ArrayList visibleEnemies; + + public Hero() { + super(); + name = "you"; + + HP = HT = 20; + STR = STARTING_STR; + awareness = 0.1f; + + belongings = new Belongings( this ); + + visibleEnemies = new ArrayList(); + } + + public int STR() { + return weakened ? STR - 2 : STR; + } + + private static final String ATTACK = "attackSkill"; + private static final String DEFENSE = "defenseSkill"; + private static final String STRENGTH = "STR"; + private static final String LEVEL = "lvl"; + private static final String EXPERIENCE = "exp"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + + heroClass.storeInBundle( bundle ); + subClass.storeInBundle( bundle ); + + bundle.put( ATTACK, attackSkill ); + bundle.put( DEFENSE, defenseSkill ); + + bundle.put( STRENGTH, STR ); + + bundle.put( LEVEL, lvl ); + bundle.put( EXPERIENCE, exp ); + + belongings.storeInBundle( bundle ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + + heroClass = HeroClass.restoreInBundle( bundle ); + subClass = HeroSubClass.restoreInBundle( bundle ); + + attackSkill = bundle.getInt( ATTACK ); + defenseSkill = bundle.getInt( DEFENSE ); + + STR = bundle.getInt( STRENGTH ); + updateAwareness(); + + lvl = bundle.getInt( LEVEL ); + exp = bundle.getInt( EXPERIENCE ); + + belongings.restoreFromBundle( bundle ); + } + + public static void preview( GamesInProgress.Info info, Bundle bundle ) { + // Refactoring needed! + Armor armor = (Armor)bundle.get( "armor" ); + info.armor = armor == null ? 0 : armor.tier; + info.level = bundle.getInt( LEVEL ); + } + + public String className() { + return subClass == null || subClass == HeroSubClass.NONE ? heroClass.title() : subClass.title(); + } + + public void live() { + Buff.affect( this, Regeneration.class ); + Buff.affect( this, Hunger.class ); + } + + public int tier() { + return belongings.armor == null ? 0 : belongings.armor.tier; + } + + public boolean shoot( Char enemy, Weapon wep ) { + + // Ugly... + usingRanged = true; + + KindOfWeapon curWep = belongings.weapon; + belongings.weapon = wep; + + boolean result = attack( enemy ); + + belongings.weapon = curWep; + usingRanged = false; + + return result; + } + + @Override + public int attackSkill( Char target ) { + + int bonus = 0; + for (Buff buff : buffs( RingOfAccuracy.Accuracy.class )) { + bonus += ((RingOfAccuracy.Accuracy)buff).level; + } + float accuracy = (bonus == 0) ? 1 : (float)Math.pow( 1.4, bonus ); + if (usingRanged && Level.distance( pos, target.pos ) == 1) { + accuracy *= 0.5f; + } + + if (belongings.weapon != null) { + return (int)(attackSkill * accuracy * belongings.weapon.acuracyFactor( this )); + } else { + return (int)(attackSkill * accuracy); + } + } + + @Override + public int defenseSkill( Char enemy ) { + + int bonus = 0; + for (Buff buff : buffs( RingOfEvasion.Evasion.class )) { + bonus += ((RingOfEvasion.Evasion)buff).level; + } + float evasion = bonus == 0 ? 1 : (float)Math.pow( 1.2, bonus ); + if (paralysed) { + evasion /= 2; + } + + int aEnc = belongings.armor != null ? belongings.armor.STR - STR() : 0; + + if (aEnc > 0) { + return (int)(defenseSkill * evasion / Math.pow( 1.5, aEnc )); + } else { + + if (heroClass == HeroClass.ROGUE) { + + if (curAction != null && subClass == HeroSubClass.FREERUNNER && !isStarving()) { + evasion *= 2; + } + + return (int)((defenseSkill - aEnc) * evasion); + } else { + return (int)(defenseSkill * evasion); + } + } + } + + @Override + public int dr() { + int dr = belongings.armor != null ? Math.max( belongings.armor.DR, 0 ) : 0; + Barkskin barkskin = buff( Barkskin.class ); + if (barkskin != null) { + dr += barkskin.level(); + } + return dr; + } + + @Override + public int damageRoll() { + int dmg; + if (belongings.weapon != null) { + dmg = belongings.weapon.damageRoll( this ); + } else { + dmg = STR() > 10 ? Random.IntRange( 1, STR() - 9 ) : 1; + } + return buff( Fury.class ) != null ? (int)(dmg * 1.5f) : dmg; + } + + @Override + public float speed() { + + int aEnc = belongings.armor != null ? belongings.armor.STR - STR() : 0; + if (aEnc > 0) { + + return (float)(super.speed() * Math.pow( 1.3, -aEnc )); + + } else { + + float speed = super.speed(); + return ((HeroSprite)sprite).sprint( subClass == HeroSubClass.FREERUNNER && !isStarving() ) ? 1.6f * speed : speed; + + } + } + + public float attackDelay() { + if (belongings.weapon != null) { + + return belongings.weapon.speedFactor( this ); + + } else { + return 1f; + } + } + + @Override + public void spend( float time ) { + int hasteLevel = 0; + for (Buff buff : buffs( RingOfHaste.Haste.class )) { + hasteLevel += ((RingOfHaste.Haste)buff).level; + } + super.spend( hasteLevel == 0 ? time : (float)(time * Math.pow( 1.1, -hasteLevel )) ); + }; + + public void spendAndNext( float time ) { + busy(); + spend( time ); + next(); + } + + @Override + public boolean act() { + + super.act(); + + if (paralysed) { + + curAction = null; + + spendAndNext( TICK ); + return false; + } + + checkVisibleMobs(); + AttackIndicator.updateState(); + + if (curAction == null) { + + if (restoreHealth) { + if (isStarving() || HP >= HT) { + restoreHealth = false; + } else { + spend( TIME_TO_REST ); next(); + return false; + } + } + + ready(); + + } else { + + restoreHealth = false; + + ready = false; + + if (curAction instanceof HeroAction.Move) { + + actMove( (HeroAction.Move)curAction ); + + } else + if (curAction instanceof HeroAction.Interact) { + + actInteract( (HeroAction.Interact)curAction ); + + } else + if (curAction instanceof HeroAction.Buy) { + + actBuy( (HeroAction.Buy)curAction ); + + }else + if (curAction instanceof HeroAction.PickUp) { + + actPickUp( (HeroAction.PickUp)curAction ); + + } else + if (curAction instanceof HeroAction.OpenChest) { + + actOpenChest( (HeroAction.OpenChest)curAction ); + + } else + if (curAction instanceof HeroAction.Unlock) { + + actUnlock( (HeroAction.Unlock)curAction ); + + } else + if (curAction instanceof HeroAction.Descend) { + + actDescend( (HeroAction.Descend)curAction ); + + } else + if (curAction instanceof HeroAction.Ascend) { + + actAscend( (HeroAction.Ascend)curAction ); + + } else + if (curAction instanceof HeroAction.Attack) { + + actAttack( (HeroAction.Attack)curAction ); + + } else + if (curAction instanceof HeroAction.Cook) { + + actCook( (HeroAction.Cook)curAction ); + + } + } + + return false; + } + + public void busy() { + ready = false; + } + + private void ready() { + sprite.idle(); + curAction = null; + ready = true; + + GameScene.ready(); + } + + public void interrupt() { + if (curAction != null && curAction.dst != pos) { + lastAction = curAction; + } + curAction = null; + } + + public void resume() { + curAction = lastAction; + lastAction = null; + act(); + } + + private void actMove( HeroAction.Move action ) { + + if (getCloser( action.dst )) { + + } else { + if (Dungeon.level.map[pos] == Terrain.SIGN) { + GameScene.show( new WndMessage( Dungeon.tip() ) ); + } + ready(); + } + } + + private void actInteract( HeroAction.Interact action ) { + + Mob.NPC npc = action.npc; + + if (Level.adjacent( pos, npc.pos )) { + + ready(); + sprite.turnTo( pos, npc.pos ); + npc.interact(); + + } else { + + if (Level.fieldOfView[npc.pos] && getCloser( npc.pos )) { + + } else { + ready(); + } + + } + } + + private void actBuy( HeroAction.Buy action ) { + int dst = action.dst; + if (pos == dst || Level.adjacent( pos, dst )) { + + ready(); + + Heap heap = Dungeon.level.heaps.get( dst ); + if (heap != null && heap.type == Type.FOR_SALE && heap.size() == 1) { + GameScene.show( new WndTradeItem( heap, true ) ); + } + + } else if (getCloser( dst )) { + + } else { + ready(); + } + } + + private void actCook( HeroAction.Cook action ) { + int dst = action.dst; + if (Dungeon.visible[dst]) { + + ready(); + AlchemyPot.operate( this, dst ); + + } else if (getCloser( dst )) { + + } else { + ready(); + } + } + + private void actPickUp( HeroAction.PickUp action ) { + int dst = action.dst; + if (pos == dst) { + + Heap heap = Dungeon.level.heaps.get( pos ); + if (heap != null) { + Item item = heap.pickUp(); + if (item.doPickUp( this )) { + + if (item instanceof Dewdrop) { + + } else { + + if ((item instanceof ScrollOfUpgrade && ((ScrollOfUpgrade)item).isKnown()) || + (item instanceof PotionOfStrength && ((PotionOfStrength)item).isKnown())) { + GLog.p( TXT_YOU_NOW_HAVE, item.name() ); + } else { + GLog.i( TXT_YOU_NOW_HAVE, item.name() ); + } + } + + if (!heap.isEmpty()) { + GLog.i( TXT_SOMETHING_ELSE ); + } + curAction = null; + } else { + Dungeon.level.drop( item, pos ).sprite.drop(); + ready(); + } + } else { + ready(); + } + + } else if (getCloser( dst )) { + + } else { + ready(); + } + } + + private void actOpenChest( HeroAction.OpenChest action ) { + int dst = action.dst; + if (Level.adjacent( pos, dst ) || pos == dst) { + + Heap heap = Dungeon.level.heaps.get( dst ); + if (heap != null && + (heap.type == Type.CHEST || heap.type == Type.TOMB || heap.type == Type.SKELETON || + heap.type == Type.LOCKED_CHEST || heap.type == Type.CRYSTAL_CHEST)) { + + theKey = null; + + if (heap.type == Type.LOCKED_CHEST || heap.type == Type.CRYSTAL_CHEST) { + + theKey = belongings.getKey( GoldenKey.class, Dungeon.depth ); + + if (theKey == null) { + GLog.w( TXT_LOCKED_CHEST ); + ready(); + return; + } + } + + switch (heap.type) { + case TOMB: + Sample.INSTANCE.play( Assets.SND_TOMB ); + Camera.main.shake( 1, 0.5f ); + break; + case SKELETON: + break; + default: + Sample.INSTANCE.play( Assets.SND_UNLOCK ); + } + + spend( Key.TIME_TO_UNLOCK ); + sprite.operate( dst ); + + } else { + ready(); + } + + } else if (getCloser( dst )) { + + } else { + ready(); + } + } + + private void actUnlock( HeroAction.Unlock action ) { + int doorCell = action.dst; + if (Level.adjacent( pos, doorCell )) { + + theKey = null; + int door = Dungeon.level.map[doorCell]; + + if (door == Terrain.LOCKED_DOOR) { + + theKey = belongings.getKey( IronKey.class, Dungeon.depth ); + + } else if (door == Terrain.LOCKED_EXIT) { + + theKey = belongings.getKey( SkeletonKey.class, Dungeon.depth ); + + } + + if (theKey != null) { + + spend( Key.TIME_TO_UNLOCK ); + sprite.operate( doorCell ); + + Sample.INSTANCE.play( Assets.SND_UNLOCK ); + + } else { + GLog.w( TXT_LOCKED_DOOR ); + ready(); + } + + } else if (getCloser( doorCell )) { + + } else { + ready(); + } + } + + private void actDescend( HeroAction.Descend action ) { + int stairs = action.dst; + if (pos == stairs && pos == Dungeon.level.exit) { + + curAction = null; + + Hunger hunger = buff( Hunger.class ); + if (hunger != null && !hunger.isStarving()) { + hunger.satisfy( -Hunger.STARVING / 10 ); + } + + InterlevelScene.mode = InterlevelScene.Mode.DESCEND; + Game.switchScene( InterlevelScene.class ); + + } else if (getCloser( stairs )) { + + } else { + ready(); + } + } + + private void actAscend( HeroAction.Ascend action ) { + int stairs = action.dst; + if (pos == stairs && pos == Dungeon.level.entrance) { + + if (Dungeon.depth == 1) { + + if (belongings.getItem( Amulet.class ) == null) { + GameScene.show( new WndMessage( TXT_LEAVE ) ); + ready(); + } else { + Dungeon.deleteGame( Dungeon.hero.heroClass, true ); + Game.switchScene( SurfaceScene.class ); + } + + } else { + + curAction = null; + + Hunger hunger = buff( Hunger.class ); + if (hunger != null && !hunger.isStarving()) { + hunger.satisfy( -Hunger.STARVING / 10 ); + } + + InterlevelScene.mode = InterlevelScene.Mode.ASCEND; + Game.switchScene( InterlevelScene.class ); + } + + } else if (getCloser( stairs )) { + + } else { + ready(); + } + } + + private void actAttack( HeroAction.Attack action ) { + + enemy = action.target; + + if (Level.adjacent( pos, enemy.pos ) && enemy.isAlive() && !pacified) { + + spend( attackDelay() ); + sprite.attack( enemy.pos ); + + } else { + + if (Level.fieldOfView[enemy.pos] && getCloser( enemy.pos )) { + + } else { + ready(); + } + + } + } + + public void rest( boolean tillHealthy ) { + spendAndNext( TIME_TO_REST ); + if (!tillHealthy) { + sprite.showStatus( CharSprite.DEFAULT, TXT_WAIT ); + } + restoreHealth = tillHealthy; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (belongings.weapon != null) { + + KindOfWeapon weapon = belongings.weapon; + weapon.proc( this, enemy, damage ); + + switch (subClass) { + case GLADIATOR: + if (weapon instanceof MeleeWeapon) { + damage += Buff.affect( this, Combo.class ).hit( enemy, damage ); + } + break; + case BATTLEMAGE: + if (weapon instanceof Wand) { + Wand wand = (Wand)weapon; + if (wand.curCharges < wand.maxCharges && damage > 0) { + + wand.curCharges++; + if (Dungeon.quickslot == wand) { + QuickSlot.refresh(); + } + + ScrollOfRecharging.charge( this ); + } + damage += wand.curCharges; + } + case SNIPER: + if (usingRanged) { + Buff.prolong( enemy, SnipersMark.class, attackDelay() * 1.1f ); + } + break; + default: + } + } + + return damage; + } + + @Override + public int defenseProc( Char enemy, int damage ) { + + RingOfThorns.Thorns thorns = buff( RingOfThorns.Thorns.class ); + if (thorns != null) { + int dmg = Random.IntRange( 0, damage ); + if (dmg > 0) { + enemy.damage( dmg, thorns ); + } + } + + Earthroot.Armor armor = buff( Earthroot.Armor.class ); + if (armor != null) { + damage = armor.absorb( damage ); + } + + if (belongings.armor != null) { + damage = belongings.armor.proc( enemy, this, damage ); + } + + return damage; + } + + @Override + public void damage( int dmg, Object src ) { + restoreHealth = false; + super.damage( dmg, src ); + + if (subClass == HeroSubClass.BERSERKER && 0 < HP && HP <= HT * Fury.LEVEL) { + Buff.affect( this, Fury.class ); + } + } + + private void checkVisibleMobs() { + + ArrayList visible = new ArrayList(); + + boolean newMob = false; + + for (Mob m : Dungeon.level.mobs) { + if (Level.fieldOfView[ m.pos ] && m.hostile) { + visible.add( m ); + if (!visibleEnemies.contains( m )) { + newMob = true; + } + } + } + + if (newMob) { + interrupt(); + restoreHealth = false; + } + + visibleEnemies = visible; + } + + public int visibleEnemies() { + return visibleEnemies.size(); + } + + public Mob visibleEnemy( int index ) { + return visibleEnemies.get( index % visibleEnemies.size() ); + } + + private boolean getCloser( final int target ) { + + if (rooted) { + return false; + } + + int step = -1; + + if (Level.adjacent( pos, target )) { + + if (Actor.findChar( target ) == null) { + if (Level.pit[target] && !flying && !Chasm.jumpConfirmed) { + Chasm.heroJump( this ); + interrupt(); + return false; + } + if (Level.passable[target] || Level.avoid[target]) { + step = target; + } + } + + } else { + + int len = Level.LENGTH; + boolean[] p = Level.passable; + boolean[] v = Dungeon.level.visited; + boolean[] m = Dungeon.level.mapped; + boolean[] passable = new boolean[len]; + for (int i=0; i < len; i++) { + passable[i] = p[i] && (v[i] || m[i]); + } + + step = Dungeon.findPath( this, pos, target, passable, Level.fieldOfView ); + } + + if (step != -1) { + + sprite.move( pos, step ); + move( step ); + spend( 1 / speed() ); + + return true; + + } else { + + return false; + + } + + } + + public void handle( int cell ) { + + if (cell == -1) { + return; + } + + Char ch; + Heap heap; + + if (Dungeon.level.map[cell] == Terrain.ALCHEMY && cell != pos) { + + curAction = new HeroAction.Cook( cell ); + + } else + if (Level.fieldOfView[cell] && (ch = Actor.findChar( cell )) instanceof Mob) { + + if (ch instanceof Mob.NPC) { + curAction = new HeroAction.Interact( (Mob.NPC)ch ); + } else { + curAction = new HeroAction.Attack( ch ); + } + + } else if ((heap = Dungeon.level.heaps.get( cell )) != null) { + + switch (heap.type) { + case HEAP: + curAction = new HeroAction.PickUp( cell ); + break; + case FOR_SALE: + curAction = heap.size() == 1 && heap.peek().price() > 0 ? + new HeroAction.Buy( cell ) : + new HeroAction.PickUp( cell ); + break; + default: + curAction = new HeroAction.OpenChest( cell ); + } + + } else if (Dungeon.level.map[cell] == Terrain.LOCKED_DOOR || Dungeon.level.map[cell] == Terrain.LOCKED_EXIT) { + + curAction = new HeroAction.Unlock( cell ); + + } else if (cell == Dungeon.level.exit) { + + curAction = new HeroAction.Descend( cell ); + + } else if (cell == Dungeon.level.entrance) { + + curAction = new HeroAction.Ascend( cell ); + + } else { + + curAction = new HeroAction.Move( cell ); + lastAction = null; + + } + + act(); + } + + public void earnExp( int exp ) { + + this.exp += exp; + + boolean levelUp = false; + while (this.exp >= maxExp()) { + this.exp -= maxExp(); + lvl++; + + HT += 5; + HP += 5; + attackSkill++; + defenseSkill++; + + if (lvl < 10) { + updateAwareness(); + } + + levelUp = true; + } + + if (levelUp) { + + GLog.p( TXT_NEW_LEVEL, lvl ); + sprite.showStatus( CharSprite.POSITIVE, TXT_LEVEL_UP ); + Sample.INSTANCE.play( Assets.SND_LEVELUP ); + + Badges.validateLevelReached(); + } + + if (subClass == HeroSubClass.WARLOCK) { + + int value = Math.min( HT - HP, 1 + (Dungeon.depth - 1) / 5 ); + if (value > 0) { + HP += value; + sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + } + + ((Hunger)buff( Hunger.class )).satisfy( 10 ); + } + } + + public int maxExp() { + return 5 + lvl * 5; + } + + void updateAwareness() { + awareness = (float)(1 - Math.pow( + (heroClass == HeroClass.ROGUE ? 0.85 : 0.90), + (1 + Math.min( lvl, 9 )) * 0.5 + )); + } + + public boolean isStarving() { + return ((Hunger)buff( Hunger.class )).isStarving(); + } + + @Override + public void add( Buff buff ) { + super.add( buff ); + + if (sprite != null) { + if (buff instanceof Burning) { + GLog.w( "You catch fire!" ); + interrupt(); + } else if (buff instanceof Paralysis) { + GLog.w( "You are paralysed!" ); + interrupt(); + } else if (buff instanceof Poison) { + GLog.w( "You are poisoned!" ); + interrupt(); + } else if (buff instanceof Ooze) { + GLog.w( "Caustic ooze eats your flesh. Wash away it!" ); + } else if (buff instanceof Roots) { + GLog.w( "You can't move!" ); + } else if (buff instanceof Weakness) { + GLog.w( "You feel weakened!" ); + } else if (buff instanceof Blindness) { + GLog.w( "You are blinded!" ); + } else if (buff instanceof Fury) { + GLog.w( "You become furious!" ); + sprite.showStatus( CharSprite.POSITIVE, "furious" ); + } else if (buff instanceof Charm) { + GLog.w( "You are charmed!" ); + } else if (buff instanceof Cripple) { + GLog.w( "You are crippled!" ); + } else if (buff instanceof Bleeding) { + GLog.w( "You are bleeding!" ); + } + + else if (buff instanceof Light) { + sprite.add( CharSprite.State.ILLUMINATED ); + } + } + + BuffIndicator.refreshHero(); + } + + @Override + public void remove( Buff buff ) { + super.remove( buff ); + + if (buff instanceof Light) { + sprite.remove( CharSprite.State.ILLUMINATED ); + } + + BuffIndicator.refreshHero(); + } + + @Override + public int stealth() { + int stealth = super.stealth(); + for (Buff buff : buffs( RingOfShadows.Shadows.class )) { + stealth += ((RingOfShadows.Shadows)buff).level; + } + return stealth; + } + + @Override + public void die( Object cause ) { + + curAction = null; + + DewVial.autoDrink( this ); + if (isAlive()) { + new Flare( 8, 32 ).color( 0xFFFF66, true ).show( sprite, 2f ) ; + return; + } + + Actor.fixTime(); + super.die( cause ); + + Ankh ankh = (Ankh)belongings.getItem( Ankh.class ); + if (ankh == null) { + + reallyDie( cause ); + + } else { + + Dungeon.deleteGame( Dungeon.hero.heroClass, false ); + GameScene.show( new WndResurrect( ankh, cause ) ); + + } + } + + public static void reallyDie( Object cause ) { + + int length = Level.LENGTH; + int[] map = Dungeon.level.map; + boolean[] visited = Dungeon.level.visited; + boolean[] discoverable = Level.discoverable; + + for (int i=0; i < length; i++) { + + int terr = map[i]; + + if (discoverable[i]) { + + visited[i] = true; + if ((Terrain.flags[terr] & Terrain.SECRET) != 0) { + Level.set( i, Terrain.discover( terr ) ); + GameScene.updateMap( i ); + } + } + } + + Bones.leave(); + + Dungeon.observe(); + + Dungeon.hero.belongings.identify(); + + GameScene.gameOver(); + + if (cause instanceof Hero.Doom) { + ((Hero.Doom)cause).onDeath(); + } + + Dungeon.deleteGame( Dungeon.hero.heroClass, true ); + } + + @Override + public void move( int step ) { + super.move( step ); + + if (!flying) { + + if (Level.water[step]) { + Sample.INSTANCE.play( Assets.SND_WATER, 1, 1, Random.Float( 0.8f, 1.25f ) ); + } else { + Sample.INSTANCE.play( Assets.SND_STEP ); + } + Dungeon.level.press( step, this ); + } + } + + @Override + public void onMotionComplete() { + + Dungeon.observe(); + search( false ); + + super.onMotionComplete(); + } + + @Override + public void onAttackComplete() { + + AttackIndicator.target( enemy ); + + attack( enemy ); + curAction = null; + + Invisibility.dispel(); + + super.onAttackComplete(); + } + + @Override + public void onOperateComplete() { + + if (curAction instanceof HeroAction.Unlock) { + + if (theKey != null) { + theKey.detach( belongings.backpack ); + theKey = null; + } + + int doorCell = ((HeroAction.Unlock)curAction).dst; + int door = Dungeon.level.map[doorCell]; + + Level.set( doorCell, door == Terrain.LOCKED_DOOR ? Terrain.DOOR : Terrain.UNLOCKED_EXIT ); + GameScene.updateMap( doorCell ); + + } else if (curAction instanceof HeroAction.OpenChest) { + + if (theKey != null) { + theKey.detach( belongings.backpack ); + theKey = null; + } + + Heap heap = Dungeon.level.heaps.get( ((HeroAction.OpenChest)curAction).dst ); + if (heap.type == Type.SKELETON) { + Sample.INSTANCE.play( Assets.SND_BONES ); + } + heap.open( this ); + } + curAction = null; + + super.onOperateComplete(); + } + + public boolean search( boolean intentional ) { + + boolean smthFound = false; + + int positive = 0; + int negative = 0; + for (Buff buff : buffs( RingOfDetection.Detection.class )) { + int bonus = ((RingOfDetection.Detection)buff).level; + if (bonus > positive) { + positive = bonus; + } else if (bonus < 0) { + negative += bonus; + } + } + int distance = 1 + positive + negative; + + float level = intentional ? (2 * awareness - awareness * awareness) : awareness; + if (distance <= 0) { + + level /= 2 - distance; + distance = 1; + } + + int cx = pos % Level.WIDTH; + int cy = pos / Level.WIDTH; + int ax = cx - distance; + if (ax < 0) { + ax = 0; + } + int bx = cx + distance; + if (bx >= Level.WIDTH) { + bx = Level.WIDTH - 1; + } + int ay = cy - distance; + if (ay < 0) { + ay = 0; + } + int by = cy + distance; + if (by >= Level.HEIGHT) { + by = Level.HEIGHT - 1; + } + + for (int y = ay; y <= by; y++) { + for (int x = ax, p = ax + y * Level.WIDTH; x <= bx; x++, p++) { + + if (Dungeon.visible[p]) { + + if (intentional) { + sprite.parent.addToBack( new CheckedCell( p ) ); + } + + if (Level.secret[p] && (intentional || Random.Float() < level)) { + + int oldValue = Dungeon.level.map[p]; + + GameScene.discoverTile( p, oldValue ); + + Level.set( p, Terrain.discover( oldValue ) ); + + GameScene.updateMap( p ); + + ScrollOfMagicMapping.discover( p ); + + smthFound = true; + } + } + } + } + + + if (intentional) { + sprite.showStatus( CharSprite.DEFAULT, TXT_SEARCH ); + sprite.operate( pos ); + if (smthFound) { + spendAndNext( Random.Float() < level ? TIME_TO_SEARCH : TIME_TO_SEARCH * 2 ); + } else { + spendAndNext( TIME_TO_SEARCH ); + } + + } + + if (smthFound) { + GLog.w( TXT_NOTICED_SMTH ); + Sample.INSTANCE.play( Assets.SND_SECRET ); + interrupt(); + } + + return smthFound; + } + + public void resurrect( int resetLevel ) { + + HP = HT; + Dungeon.gold = 0; + exp = 0; + + belongings.resurrect( resetLevel ); + + live(); + } + + @Override + public HashSet> resistances() { + RingOfElements.Resistance r = buff( RingOfElements.Resistance.class ); + return r == null ? super.resistances() : r.resistances(); + } + + @Override + public HashSet> immunities() { + GasesImmunity buff = buff( GasesImmunity.class ); + return buff == null ? super.immunities() : GasesImmunity.IMMUNITIES; + } + + public static interface Doom { + public void onDeath(); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/hero/HeroAction.java b/src/com/watabou/pixeldungeon/actors/hero/HeroAction.java new file mode 100644 index 0000000000..1c51ff5a44 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/hero/HeroAction.java @@ -0,0 +1,89 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.hero; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Mob; + + +public class HeroAction { + + public int dst; + + public static class Move extends HeroAction { + public Move( int dst ) { + this.dst = dst; + } + } + + public static class PickUp extends HeroAction { + public PickUp( int dst ) { + this.dst = dst; + } + } + + public static class OpenChest extends HeroAction { + public OpenChest( int dst ) { + this.dst = dst; + } + } + + public static class Buy extends HeroAction { + public Buy( int dst ) { + this.dst = dst; + } + } + + public static class Interact extends HeroAction { + public Mob.NPC npc; + public Interact( Mob.NPC npc ) { + this.npc = npc; + } + } + + public static class Unlock extends HeroAction { + public Unlock( int door ) { + this.dst = door; + } + } + + public static class Descend extends HeroAction { + public Descend( int stairs ) { + this.dst = stairs; + } + } + + public static class Ascend extends HeroAction { + public Ascend( int stairs ) { + this.dst = stairs; + } + } + + public static class Cook extends HeroAction { + public Cook( int pot ) { + this.dst = pot; + } + } + + public static class Attack extends HeroAction { + public Char target; + public Attack( Char target ) { + this.target = target; + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/hero/HeroClass.java b/src/com/watabou/pixeldungeon/actors/hero/HeroClass.java new file mode 100644 index 0000000000..7733dd01d0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/hero/HeroClass.java @@ -0,0 +1,221 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.hero; + +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.TomeOfMastery; +import com.watabou.pixeldungeon.items.armor.ClothArmor; +import com.watabou.pixeldungeon.items.food.Food; +import com.watabou.pixeldungeon.items.potions.PotionOfStrength; +import com.watabou.pixeldungeon.items.rings.RingOfShadows; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfIdentify; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfMagicMapping; +import com.watabou.pixeldungeon.items.wands.WandOfMagicMissile; +import com.watabou.pixeldungeon.items.weapon.melee.Dagger; +import com.watabou.pixeldungeon.items.weapon.melee.Knuckles; +import com.watabou.pixeldungeon.items.weapon.melee.ShortSword; +import com.watabou.pixeldungeon.items.weapon.missiles.Dart; +import com.watabou.pixeldungeon.items.weapon.missiles.Boomerang; +import com.watabou.utils.Bundle; + +public enum HeroClass { + + WARRIOR( "warrior" ), MAGE( "mage" ), ROGUE( "rogue" ), HUNTRESS( "huntress" ); + + private String title; + + private HeroClass( String title ) { + this.title = title; + } + + public static final String[] WAR_PERKS = { + "Warriors start with 11 points of Strength.", + "Warriors start with a unique short sword. This sword can be later \"reforged\" to upgrade another melee weapon.", + "Warriors are less proficient with missile weapons.", + "Any piece of food restores some health when eaten.", + "Potions of Strength are identified from the beginning.", + }; + + public static final String[] MAG_PERKS = { + "Mages start with a unique Wand of Magic Missile. This wand can be later \"disenchanted\" to upgrade another wand.", + "Mages recharge their wands faster.", + "When eaten, any piece of food restores 1 charge for all wands in the inventory.", + "Mages can use wands as a melee weapon.", + "Scrolls of Identify are identified from the beginning." + }; + + public static final String[] ROG_PERKS = { + "Rogues start with a Ring of Shadows+1.", + "Rogues identify a type of a ring on equipping it.", + "Rogues are proficient with light armor, dodging better while wearing one.", + "Rogues are proficient in detecting hidden doors and traps.", + "Rogues can go without food longer.", + "Scrolls of Magic Mapping are identified from the beginning." + }; + + public static final String[] HUN_PERKS = { + "Huntresses start with 15 points of Health.", + "Huntresses start with a unique upgradeable boomerang.", + "Huntresses are proficient with missile weapons and get damage bonus for excessive strength when using them.", + "Huntresses gain more health from dewdrops.", + "Huntresses sense neighbouring monsters even if they are hidden behind obstacles." + }; + + public void initHero( Hero hero ) { + + hero.heroClass = this; + + switch (this) { + case WARRIOR: + initWarrior( hero ); + break; + + case MAGE: + initMage( hero ); + break; + + case ROGUE: + initRogue( hero ); + break; + + case HUNTRESS: + initHuntress( hero ); + break; + } + + hero.updateAwareness(); + } + + private static void initWarrior( Hero hero ) { + hero.STR = hero.STR + 1; + + (hero.belongings.weapon = new ShortSword()).identify(); + (hero.belongings.armor = new ClothArmor()).identify(); + new Dart( 8 ).identify().collect(); + new Food().identify().collect(); + + if (Badges.isUnlocked( Badges.Badge.MASTERY_WARRIOR )) { + new TomeOfMastery().collect(); + } + + Dungeon.quickslot = Dart.class; + + new PotionOfStrength().setKnown(); + } + + private static void initMage( Hero hero ) { + (hero.belongings.weapon = new Knuckles()).identify(); + (hero.belongings.armor = new ClothArmor()).identify(); + new Food().identify().collect(); + + WandOfMagicMissile wand = new WandOfMagicMissile(); + wand.identify().collect(); + + if (Badges.isUnlocked( Badges.Badge.MASTERY_MAGE )) { + new TomeOfMastery().collect(); + } + + Dungeon.quickslot = wand; + + new ScrollOfIdentify().setKnown(); + } + + private static void initRogue( Hero hero ) { + (hero.belongings.weapon = new Dagger()).identify(); + (hero.belongings.armor = new ClothArmor()).identify(); + (hero.belongings.ring1 = new RingOfShadows()).upgrade().identify(); + new Dart( 8 ).identify().collect(); + new Food().identify().collect(); + + hero.belongings.ring1.activate( hero ); + + if (Badges.isUnlocked( Badges.Badge.MASTERY_ROGUE )) { + new TomeOfMastery().collect(); + } + + Dungeon.quickslot = Dart.class; + + new ScrollOfMagicMapping().setKnown(); + } + + private static void initHuntress( Hero hero ) { + + hero.HP = (hero.HT -= 5); + + (hero.belongings.weapon = new Dagger()).identify(); + (hero.belongings.armor = new ClothArmor()).identify(); + Boomerang boomerang = new Boomerang(); + boomerang.identify().collect(); + new Food().identify().collect(); + + if (Badges.isUnlocked( Badges.Badge.MASTERY_HUNTRESS )) { + new TomeOfMastery().collect(); + } + + Dungeon.quickslot = boomerang; + } + + public String title() { + return title; + } + + public String spritesheet() { + + switch (this) { + case WARRIOR: + return Assets.WARRIOR; + case MAGE: + return Assets.MAGE; + case ROGUE: + return Assets.ROGUE; + case HUNTRESS: + return Assets.HUNTRESS; + } + + return null; + } + + public String[] perks() { + + switch (this) { + case WARRIOR: + return WAR_PERKS; + case MAGE: + return MAG_PERKS; + case ROGUE: + return ROG_PERKS; + case HUNTRESS: + return HUN_PERKS; + } + + return null; + } + + private static final String CLASS = "class"; + + public void storeInBundle( Bundle bundle ) { + bundle.put( CLASS, toString() ); + } + + public static HeroClass restoreInBundle( Bundle bundle ) { + String value = bundle.getString( CLASS ); + return value.length() > 0 ? valueOf( value ) : ROGUE; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/hero/HeroSubClass.java b/src/com/watabou/pixeldungeon/actors/hero/HeroSubClass.java new file mode 100644 index 0000000000..de594fc4e6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/hero/HeroSubClass.java @@ -0,0 +1,88 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.hero; + +import com.watabou.utils.Bundle; + +public enum HeroSubClass { + + NONE( null, null ), + + GLADIATOR( "gladiator", + "A successful attack with a melee weapon allows the _Gladiator_ to start a combo, " + + "in which every next successful hit inflicts more damage." ), + + BERSERKER( "berserker", + "When severely wounded, the _Berserker_ enters a state of wild fury " + + "significantly increasing his damage output." ), + + WARLOCK( "warlock", + "After killing an enemy the _Warlock_ consumes its soul. " + + "It heals his wounds and satisfies his hunger." ), + + BATTLEMAGE( "battlemage", + "When fighting with a wand in his hands, the _Battlemage_ inflicts additional damage depending " + + "on the current number of charges. Every successful hit restores 1 charge to this wand." ), + + ASSASSIN( "assassin", + "When performing a surprise attack, the _Assassin_ inflicts additional damage to his target." ), + + FREERUNNER( "freerunner", + "The _Freerunner_ can move almost twice faster, than most of the monsters. When he " + + "is running, the Freerunner is much harder to hit. For that he must be unencumbered and not starving." ), + + SNIPER( "sniper", + "_Snipers_ are able to detect weak points in an enemy's armor, " + + "effectively ignoring it when using a missile weapon." ), + + WARDEN( "warden", + "Having a strong connection with forces of nature gives _Wardens_ an ability to gather dewdrops and " + + "seeds from plants. Also trampling a high grass grants them a temporary armor buff." ); + + private String title; + private String desc; + + private HeroSubClass( String title, String desc ) { + this.title = title; + this.desc = desc; + } + + public String title() { + return title; + } + + public String desc() { + return desc; + } + + private static final String SUBCLASS = "subClass"; + + public void storeInBundle( Bundle bundle ) { + bundle.put( SUBCLASS, toString() ); + } + + public static HeroSubClass restoreInBundle( Bundle bundle ) { + String value = bundle.getString( SUBCLASS ); + try { + return valueOf( value ); + } catch (Exception e) { + return NONE; + } + } + +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Acidic.java b/src/com/watabou/pixeldungeon/actors/mobs/Acidic.java new file mode 100644 index 0000000000..82f73399df --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Acidic.java @@ -0,0 +1,48 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.sprites.AcidicSprite; +import com.watabou.utils.Random; + +public class Acidic extends Scorpio { + + { + name = "acidic scorpio"; + spriteClass = AcidicSprite.class; + } + + @Override + public int defenseProc( Char enemy, int damage ) { + + int dmg = Random.IntRange( 0, damage ); + if (dmg > 0) { + enemy.damage( dmg, this ); + } + + return super.defenseProc( enemy, damage ); + } + + @Override + public void die( Object cause ) { + super.die( cause ); + Badges.validateRare( this ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Albino.java b/src/com/watabou/pixeldungeon/actors/mobs/Albino.java new file mode 100644 index 0000000000..8dd19bb723 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Albino.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.sprites.AlbinoSprite; +import com.watabou.utils.Random; + +public class Albino extends Rat { + + { + name = "albino rat"; + spriteClass = AlbinoSprite.class; + + HP = HT = 15; + } + + @Override + public void die( Object cause ) { + super.die( cause ); + Badges.validateRare( this ); + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 2 ) == 0) { + Buff.affect( enemy, Bleeding.class ).set( damage ); + } + + return damage; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Bandit.java b/src/com/watabou/pixeldungeon/actors/mobs/Bandit.java new file mode 100644 index 0000000000..5245bfadbd --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Bandit.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.BanditSprite; +import com.watabou.utils.Random; + +public class Bandit extends Thief { + + public Item item; + + { + name = "crazy bandit"; + spriteClass = BanditSprite.class; + } + + @Override + protected boolean steal( Hero hero ) { + if (super.steal( hero )) { + + Buff.prolong( enemy, Blindness.class, Random.Int( 5, 12 ) ); + Dungeon.observe(); + + return true; + } else { + return false; + } + } + + @Override + public void die( Object cause ) { + super.die( cause ); + Badges.validateRare( this ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Bat.java b/src/com/watabou/pixeldungeon/actors/mobs/Bat.java new file mode 100644 index 0000000000..4da00ce09c --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Bat.java @@ -0,0 +1,97 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.potions.PotionOfHealing; +import com.watabou.pixeldungeon.items.weapon.enchantments.Leech; +import com.watabou.pixeldungeon.sprites.BatSprite; +import com.watabou.utils.Random; + +public class Bat extends Mob { + + { + name = "vampire bat"; + spriteClass = BatSprite.class; + + HP = HT = 30; + defenseSkill = 15; + baseSpeed = 2f; + + EXP = 7; + maxLvl = 15; + + flying = true; + + loot = new PotionOfHealing(); + lootChance = 0.125f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 6, 12 ); + } + + @Override + public int attackSkill( Char target ) { + return 16; + } + + @Override + public int dr() { + return 4; + } + + @Override + public String defenseVerb() { + return "evaded"; + } + + @Override + public int attackProc( Char enemy, int damage ) { + + int reg = Math.min( damage, HT - HP ); + + if (reg > 0) { + HP += reg; + sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + } + + return damage; + } + + @Override + public String description() { + return + "These brisk and tenacious inhabitants of cave domes may defeat much larger opponents by " + + "replenishing their health with each successful attack."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( Leech.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Bestiary.java b/src/com/watabou/pixeldungeon/actors/mobs/Bestiary.java new file mode 100644 index 0000000000..ce0824109b --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Bestiary.java @@ -0,0 +1,183 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.utils.Random; + +public class Bestiary { + + public static Mob mob( int depth ) { + @SuppressWarnings("unchecked") + Class cl = (Class)mobClass( depth ); + try { + return cl.newInstance(); + } catch (Exception e) { + return null; + } + } + + public static Mob mutable( int depth ) { + @SuppressWarnings("unchecked") + Class cl = (Class)mobClass( depth ); + + if (Random.Int( 30 ) == 0) { + if (cl == Rat.class) { + cl = Albino.class; + } else if (cl == Thief.class) { + cl = Bandit.class; + } else if (cl == Brute.class) { + cl = Shielded.class; + } else if (cl == Monk.class) { + cl = Senior.class; + } else if (cl == Scorpio.class) { + cl = Acidic.class; + } + } + + try { + return cl.newInstance(); + } catch (Exception e) { + return null; + } + } + + private static Class mobClass( int depth ) { + + float[] chances; + Class[] classes; + + switch (depth) { + case 1: + chances = new float[]{ 1 }; + classes = new Class[]{ Rat.class }; + break; + case 2: + chances = new float[]{ 1, 1 }; + classes = new Class[]{ Rat.class, Gnoll.class }; + break; + case 3: + chances = new float[]{ 1, 2, 1, 0.02f }; + classes = new Class[]{ Rat.class, Gnoll.class, Crab.class, Swarm.class }; + break; + case 4: + chances = new float[]{ 1, 2, 3, 0.02f, 0.01f, 0.01f }; + classes = new Class[]{ Rat.class, Gnoll.class, Crab.class, Swarm.class, Skeleton.class, Thief.class }; + break; + + case 5: + chances = new float[]{ 1 }; + classes = new Class[]{ Goo.class }; + break; + + case 6: + chances = new float[]{ 4, 2, 1, 0.2f }; + classes = new Class[]{ Skeleton.class, Thief.class, Swarm.class, Shaman.class }; + break; + case 7: + chances = new float[]{ 3, 1, 1, 1 }; + classes = new Class[]{ Skeleton.class, Shaman.class, Thief.class, Swarm.class }; + break; + case 8: + chances = new float[]{ 3, 2, 1, 1, 1, 0.02f }; + classes = new Class[]{ Skeleton.class, Shaman.class, Gnoll.class, Thief.class, Swarm.class, Bat.class }; + break; + case 9: + chances = new float[]{ 3, 3, 1, 1, 0.02f, 0.01f }; + classes = new Class[]{ Skeleton.class, Shaman.class, Thief.class, Swarm.class, Bat.class, Brute.class }; + break; + + case 10: + chances = new float[]{ 1 }; + classes = new Class[]{ Tengu.class }; + break; + + case 11: + chances = new float[]{ 1, 0.2f }; + classes = new Class[]{ Bat.class, Brute.class }; + break; + case 12: + chances = new float[]{ 1, 1, 0.2f }; + classes = new Class[]{ Bat.class, Brute.class, Spinner.class }; + break; + case 13: + chances = new float[]{ 1, 3, 1, 1, 0.02f }; + classes = new Class[]{ Bat.class, Brute.class, Shaman.class, Spinner.class, Elemental.class }; + break; + case 14: + chances = new float[]{ 1, 3, 1, 4, 0.02f, 0.01f }; + classes = new Class[]{ Bat.class, Brute.class, Shaman.class, Spinner.class, Elemental.class, Monk.class }; + break; + + case 15: + chances = new float[]{ 1 }; + classes = new Class[]{ DM300.class }; + break; + + case 16: + chances = new float[]{ 1, 1, 0.2f }; + classes = new Class[]{ Elemental.class, Warlock.class, Monk.class }; + break; + case 17: + chances = new float[]{ 1, 1, 1 }; + classes = new Class[]{ Elemental.class, Monk.class, Warlock.class }; + break; + case 18: + chances = new float[]{ 1, 2, 1, 1 }; + classes = new Class[]{ Elemental.class, Monk.class, Golem.class, Warlock.class }; + break; + case 19: + chances = new float[]{ 1, 2, 3, 1, 0.02f }; + classes = new Class[]{ Elemental.class, Monk.class, Golem.class, Warlock.class, Succubus.class }; + break; + + case 20: + chances = new float[]{ 1 }; + classes = new Class[]{ King.class }; + break; + + case 22: + chances = new float[]{ 1, 1 }; + classes = new Class[]{ Succubus.class, Eye.class }; + break; + case 23: + chances = new float[]{ 1, 2, 1 }; + classes = new Class[]{ Succubus.class, Eye.class, Scorpio.class }; + break; + case 24: + chances = new float[]{ 1, 2, 3 }; + classes = new Class[]{ Succubus.class, Eye.class, Scorpio.class }; + break; + + case 25: + chances = new float[]{ 1 }; + classes = new Class[]{ Yog.class }; + break; + + default: + chances = new float[]{ 1 }; + classes = new Class[]{ Eye.class }; + } + + return classes[ Random.chances( chances )]; + } + + public static boolean isUnique( Char mob ) { + return mob instanceof Goo || mob instanceof Tengu || mob instanceof DM300 || mob instanceof King || mob instanceof Yog; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Brute.java b/src/com/watabou/pixeldungeon/actors/mobs/Brute.java new file mode 100644 index 0000000000..acc104c513 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Brute.java @@ -0,0 +1,105 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.sprites.BruteSprite; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Brute extends Mob { + + private static final String TXT_ENRAGED = "%s becomes enraged!"; + + { + name = "gnoll brute"; + spriteClass = BruteSprite.class; + + HP = HT = 40; + defenseSkill = 15; + + EXP = 8; + maxLvl = 15; + + loot = Gold.class; + lootChance = 0.5f; + } + + private boolean enraged = false; + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + enraged = HP < HT / 4; + } + + @Override + public int damageRoll() { + return enraged ? + Random.NormalIntRange( 10, 40 ) : + Random.NormalIntRange( 8, 18 ); + } + + @Override + public int attackSkill( Char target ) { + return 20; + } + + @Override + public int dr() { + return 8; + } + + @Override + public void damage( int dmg, Object src ) { + super.damage( dmg, src ); + + if (isAlive() && !enraged && HP < HT / 4) { + enraged = true; + spend( TICK ); + if (Dungeon.visible[pos]) { + GLog.w( TXT_ENRAGED, name ); + sprite.showStatus( CharSprite.NEGATIVE, "enraged" ); + } + } + } + + @Override + public String description() { + return + "Brutes are the largest, strongest and toughest of all gnolls. When severely wounded, " + + "they go berserk, inflicting even more damage to their enemies."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Terror.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Crab.java b/src/com/watabou/pixeldungeon/actors/mobs/Crab.java new file mode 100644 index 0000000000..53ccd7ce29 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Crab.java @@ -0,0 +1,76 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.npcs.Ghost; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.sprites.CrabSprite; +import com.watabou.utils.Random; + +public class Crab extends Mob { + + { + name = "sewer crab"; + spriteClass = CrabSprite.class; + + HP = HT = 15; + defenseSkill = 5; + baseSpeed = 2f; + + EXP = 3; + maxLvl = 9; + + loot = new MysteryMeat(); + lootChance = 0.167f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 3, 6 ); + } + + @Override + public int attackSkill( Char target ) { + return 12; + } + + @Override + public int dr() { + return 4; + } + + @Override + public String defenseVerb() { + return "parried"; + } + + @Override + public void die( Object cause ) { + Ghost.Quest.process( pos ); + super.die( cause ); + } + + @Override + public String description() { + return + "These huge crabs are at the top of the food chain in the sewers. " + + "They are extremely fast and their thick exoskeleton can withstand " + + "heavy blows."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/DM300.java b/src/com/watabou/pixeldungeon/actors/mobs/DM300.java new file mode 100644 index 0000000000..1e943b2c83 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/DM300.java @@ -0,0 +1,173 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.items.rings.RingOfThorns; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.DM300Sprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Random; + +public class DM300 extends Mob { + + { + name = "DM-300"; + spriteClass = DM300Sprite.class; + + HP = HT = 200; + EXP = 30; + defenseSkill = 18; + + loot = new RingOfThorns().random(); + lootChance = 0.333f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 18, 24 ); + } + + @Override + public int attackSkill( Char target ) { + return 28; + } + + @Override + public int dr() { + return 10; + } + + @Override + public boolean act() { + + GameScene.add( Blob.seed( pos, 30, ToxicGas.class ) ); + + return super.act(); + } + + @Override + public void move( int step ) { + super.move( step ); + + if (Dungeon.level.map[step] == Terrain.INACTIVE_TRAP && HP < HT) { + + HP += Random.Int( 1, HT - HP ); + sprite.emitter().burst( ElmoParticle.FACTORY, 5 ); + + if (Dungeon.visible[step] && Dungeon.hero.isAlive()) { + GLog.n( "DM-300 repairs itself!" ); + } + } + + int[] cells = { + step-1, step+1, step-Level.WIDTH, step+Level.WIDTH, + step-1-Level.WIDTH, + step-1+Level.WIDTH, + step+1-Level.WIDTH, + step+1+Level.WIDTH + }; + int cell = cells[Random.Int( cells.length )]; + + if (Dungeon.visible[cell]) { + CellEmitter.get( cell ).start( Speck.factory( Speck.ROCK ), 0.07f, 10 ); + Camera.main.shake( 3, 0.7f ); + Sample.INSTANCE.play( Assets.SND_ROCKS ); + + if (Level.water[cell]) { + GameScene.ripple( cell ); + } else if (Dungeon.level.map[cell] == Terrain.EMPTY) { + Level.set( cell, Terrain.EMPTY_DECO ); + GameScene.updateMap( cell ); + } + } + + Char ch = Actor.findChar( cell ); + if (ch != null && ch != this) { + Buff.prolong( ch, Paralysis.class, 2 ); + } + } + + @Override + public void die( Object cause ) { + + super.die( cause ); + + GameScene.bossSlain(); + Dungeon.level.drop( new SkeletonKey( Dungeon.depth ), pos ).sprite.drop(); + + Badges.validateBossSlain(); + + yell( "Mission failed. Shutting down." ); + } + + @Override + public void notice() { + super.notice(); + yell( "Unauthorised personnel detected." ); + } + + @Override + public String description() { + return + "This machine was created by the Dwarves several centuries ago. Later, Dwarves started to replace machines with " + + "golems, elementals and even demons. Eventually it led their civilization to the decline. The DM-300 and similar " + + "machines were typically used for construction and mining, and in some cases, for city defense."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( ToxicGas.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Elemental.java b/src/com/watabou/pixeldungeon/actors/mobs/Elemental.java new file mode 100644 index 0000000000..6954ce9d1b --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Elemental.java @@ -0,0 +1,110 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Frost; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.potions.PotionOfLiquidFlame; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.wands.WandOfFirebolt; +import com.watabou.pixeldungeon.items.weapon.enchantments.Fire; +import com.watabou.pixeldungeon.sprites.ElementalSprite; +import com.watabou.utils.Random; + +public class Elemental extends Mob { + + { + name = "fire elemental"; + spriteClass = ElementalSprite.class; + + HP = HT = 65; + defenseSkill = 20; + + EXP = 10; + maxLvl = 20; + + flying = true; + + loot = new PotionOfLiquidFlame(); + lootChance = 0.1f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 16, 20 ); + } + + @Override + public int attackSkill( Char target ) { + return 25; + } + + @Override + public int dr() { + return 5; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 2 ) == 0) { + Buff.affect( enemy, Burning.class ).reignite( enemy ); + } + + return damage; + } + + @Override + public void add( Buff buff ) { + if (buff instanceof Burning) { + if (HP < HT) { + HP++; + sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + } + } else { + if (buff instanceof Frost) { + damage( Random.NormalIntRange( 1, HT * 2 / 3 ), buff ); + } + super.add( buff ); + } + } + + @Override + public String description() { + return + "Wandering fire elementals are a byproduct of summoning greater entities. " + + "They are too chaotic in their nature to be controlled by even the most powerful demonologist."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Burning.class ); + IMMUNITIES.add( Fire.class ); + IMMUNITIES.add( WandOfFirebolt.class ); + IMMUNITIES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Eye.java b/src/com/watabou/pixeldungeon/actors/mobs/Eye.java new file mode 100644 index 0000000000..79c24982a5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Eye.java @@ -0,0 +1,174 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.PurpleParticle; +import com.watabou.pixeldungeon.items.Dewdrop; +import com.watabou.pixeldungeon.items.wands.WandOfDisintegration; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.items.weapon.enchantments.Leech; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.EyeSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Random; + +public class Eye extends Mob { + + private static final String TXT_DEATHGAZE_KILLED = "%s's deathgaze killed you..."; + + { + name = "evil eye"; + spriteClass = EyeSprite.class; + + HP = HT = 100; + defenseSkill = 20; + viewDistance = Light.DISTANCE; + + EXP = 13; + maxLvl = 25; + + flying = true; + + loot = new Dewdrop(); + lootChance = 0.5f; + } + + @Override + public int dr() { + return 10; + } + + private int hitCell; + + @Override + protected boolean canAttack( Char enemy ) { + + hitCell = Ballistica.cast( pos, enemy.pos, true, false ); + + for (int i=1; i < Ballistica.distance; i++) { + if (Ballistica.trace[i] == enemy.pos) { + return true; + } + } + return false; + } + + @Override + public int attackSkill( Char target ) { + return 30; + } + + @Override + protected float attackDelay() { + return 1.6f; + } + + @Override + protected boolean doAttack( Char enemy ) { + + spend( attackDelay() ); + + boolean rayVisible = false; + + for (int i=0; i < Ballistica.distance; i++) { + if (Dungeon.visible[Ballistica.trace[i]]) { + rayVisible = true; + } + } + + if (rayVisible) { + sprite.attack( hitCell ); + return false; + } else { + attack( enemy ); + return true; + } + } + + @Override + public boolean attack( Char enemy ) { + + for (int i=1; i < Ballistica.distance; i++) { + + int pos = Ballistica.trace[i]; + + Char ch = Actor.findChar( pos ); + if (ch == null) { + continue; + } + + if (hit( this, ch, true )) { + ch.damage( Random.NormalIntRange( 14, 20 ), this ); + + if (Dungeon.visible[pos]) { + ch.sprite.flash(); + CellEmitter.center( pos ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) ); + } + + if (!ch.isAlive() && ch == Dungeon.hero) { + Dungeon.fail( Utils.format( ResultDescriptions.MOB, Utils.indefinite( name ), Dungeon.depth ) ); + GLog.n( TXT_DEATHGAZE_KILLED, name ); + } + } else { + ch.sprite.showStatus( CharSprite.NEUTRAL, ch.defenseVerb() ); + } + } + + return true; + } + + @Override + public String description() { + return + "One of this demon's other names is \"orb of hatred\", because when it sees an enemy, " + + "it uses its deathgaze recklessly, often ignoring its allies and wounding them."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( WandOfDisintegration.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( Leech.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Terror.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Gnoll.java b/src/com/watabou/pixeldungeon/actors/mobs/Gnoll.java new file mode 100644 index 0000000000..caa13a4708 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Gnoll.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.npcs.Ghost; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.sprites.GnollSprite; +import com.watabou.utils.Random; + +public class Gnoll extends Mob { + + { + name = "gnoll scout"; + spriteClass = GnollSprite.class; + + HP = HT = 12; + defenseSkill = 4; + + EXP = 2; + maxLvl = 8; + + loot = Gold.class; + lootChance = 0.5f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 2, 5 ); + } + + @Override + public int attackSkill( Char target ) { + return 11; + } + + @Override + public int dr() { + return 2; + } + + @Override + public void die( Object cause ) { + Ghost.Quest.process( pos ); + super.die( cause ); + } + + @Override + public String description() { + return + "Gnolls are hyena-like humanoids. They dwell in sewers and dungeons, venturing up to raid the surface from time to time. " + + "Gnoll scouts are regular members of their pack, they are not as strong as brutes and not as intelligent as shamans."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Golem.java b/src/com/watabou/pixeldungeon/actors/mobs/Golem.java new file mode 100644 index 0000000000..f1e633a2b7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Golem.java @@ -0,0 +1,105 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Sleep; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.sprites.GolemSprite; +import com.watabou.utils.Random; + +public class Golem extends Mob { + + { + name = "golem"; + spriteClass = GolemSprite.class; + + HP = HT = 85; + defenseSkill = 18; + + EXP = 12; + maxLvl = 22; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 20, 40 ); + } + + @Override + public int attackSkill( Char target ) { + return 28; + } + + @Override + protected float attackDelay() { + return 1.5f; + } + + @Override + public int dr() { + return 12; + } + + @Override + public String defenseVerb() { + return "blocked"; + } + + @Override + public void die( Object cause ) { + Imp.Quest.process( this ); + + super.die( cause ); + } + + @Override + public String description() { + return + "The Dwarves tried to combine their knowledge of mechanisms with their newfound power of elemental binding. " + + "They used spirits of earth as the \"soul\" for the mechanical bodies of golems, which were believed to be " + + "most controllable of all. Despite this, the tiniest mistake in the ritual could cause an outbreak."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Amok.class ); + IMMUNITIES.add( Terror.class ); + IMMUNITIES.add( Sleep.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Goo.java b/src/com/watabou/pixeldungeon/actors/mobs/Goo.java new file mode 100644 index 0000000000..041a240011 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Goo.java @@ -0,0 +1,188 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Ooze; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.LloydsBeacon; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.SewerBossLevel; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.GooSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Random; + +public class Goo extends Mob { + + private static final float PUMP_UP_DELAY = 2f; + + { + name = "Goo"; + HP = HT = 80; + EXP = 10; + defenseSkill = 12; + spriteClass = GooSprite.class; + + loot = new LloydsBeacon(); + lootChance = 0.333f; + } + + private boolean pumpedUp = false; + + @Override + public int damageRoll() { + if (pumpedUp) { + return Random.NormalIntRange( 5, 30 ); + } else { + return Random.NormalIntRange( 2, 12 ); + } + } + + @Override + public int attackSkill( Char target ) { + return pumpedUp ? 30 : 15; + } + + @Override + public int dr() { + return 2; + } + + @Override + public boolean act() { + + if (Level.water[pos] && HP < HT) { + sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + HP++; + } + + return super.act(); + } + + @Override + protected boolean canAttack( Char enemy ) { + return pumpedUp ? distance( enemy ) <= 2 : super.canAttack(enemy); + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 3 ) == 0) { + Buff.affect( enemy, Ooze.class ); + enemy.sprite.burst( 0x000000, 5 ); + } + + if (pumpedUp) { + Camera.main.shake( 3, 0.2f ); + } + + return damage; + } + + @Override + protected boolean doAttack( Char enemy ) { + if (pumpedUp || Random.Int( 3 ) > 0) { + + return super.doAttack( enemy ); + + } else { + + pumpedUp = true; + spend( PUMP_UP_DELAY ); + + ((GooSprite)sprite).pumpUp(); + + if (Dungeon.visible[pos]) { + sprite.showStatus( CharSprite.NEGATIVE, "!!!" ); + GLog.n( "Goo is pumping itself up!" ); + } + + return true; + } + } + + @Override + public boolean attack( Char enemy ) { + boolean result = super.attack( enemy ); + pumpedUp = false; + return result; + } + + @Override + protected boolean getCloser( int target ) { + pumpedUp = false; + return super.getCloser( target ); + } + + @Override + public void move( int step ) { + ((SewerBossLevel)Dungeon.level).seal(); + super.move( step ); + } + + @Override + public void die( Object cause ) { + + super.die( cause ); + + ((SewerBossLevel)Dungeon.level).unseal(); + + GameScene.bossSlain(); + Dungeon.level.drop( new SkeletonKey( Dungeon.depth ), pos ).sprite.drop(); + + Badges.validateBossSlain(); + + yell( "glurp... glurp..." ); + } + + @Override + public void notice() { + super.notice(); + yell( "GLURP-GLURP!" ); + } + + @Override + public String description() { + return + "Little known about The Goo. It's quite possible that it is not even a creature, but rather a " + + "conglomerate of substances from the sewers that gained rudiments of free will."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( ToxicGas.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/King.java b/src/com/watabou/pixeldungeon/actors/mobs/King.java new file mode 100644 index 0000000000..dc534c7de8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/King.java @@ -0,0 +1,328 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.ArmorKit; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.items.wands.WandOfDisintegration; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.CityBossLevel; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.KingSprite; +import com.watabou.pixeldungeon.sprites.UndeadSprite; +import com.watabou.utils.Bundle; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; + +public class King extends Mob { + + private static final int MAX_ARMY_SIZE = 5; + + { + name = "King of Dwarves"; + spriteClass = KingSprite.class; + + HP = HT = 300; + EXP = 40; + defenseSkill = 25; + + Undead.count = 0; + } + + private boolean nextPedestal = true; + + private static final String PEDESTAL = "pedestal"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( PEDESTAL, nextPedestal ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + nextPedestal = bundle.getBoolean( PEDESTAL ); + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 20, 38 ); + } + + @Override + public int attackSkill( Char target ) { + return 32; + } + + @Override + public int dr() { + return 14; + } + + @Override + public String defenseVerb() { + return "parried"; + } + + @Override + protected boolean getCloser( int target ) { + return canTryToSummon() ? + super.getCloser( CityBossLevel.pedestal( nextPedestal ) ) : + super.getCloser( target ); + } + + @Override + protected boolean canAttack( Char enemy ) { + return canTryToSummon() ? + pos == CityBossLevel.pedestal( nextPedestal ) : + Level.adjacent( pos, enemy.pos ); + } + + private boolean canTryToSummon() { + if (Undead.count < maxArmySize()) { + Char ch = Actor.findChar( CityBossLevel.pedestal( nextPedestal ) ); + return ch == this || ch == null; + } else { + return false; + } + } + + @Override + public boolean attack( Char enemy ) { + if (canTryToSummon() && pos == CityBossLevel.pedestal( nextPedestal )) { + summon(); + return true; + } else { + if (Actor.findChar( CityBossLevel.pedestal( nextPedestal ) ) == enemy) { + nextPedestal = !nextPedestal; + } + return super.attack(enemy); + } + } + + @Override + public void die( Object cause ) { + + GameScene.bossSlain(); + Dungeon.level.drop( new ArmorKit(), pos ).sprite.drop(); + Dungeon.level.drop( new SkeletonKey( Dungeon.depth ), pos ).sprite.drop(); + + super.die( cause ); + + Badges.validateBossSlain(); + + yell( "You cannot kill me, " + Dungeon.hero.heroClass.title() + "... I am... immortal..." ); + } + + private int maxArmySize() { + return 1 + MAX_ARMY_SIZE * (HT - HP) / HT; + } + + private void summon() { + + nextPedestal = !nextPedestal; + + sprite.centerEmitter().start( Speck.factory( Speck.SCREAM ), 0.4f, 2 ); + Sample.INSTANCE.play( Assets.SND_CHALLENGE ); + + boolean[] passable = Level.passable.clone(); + for (Actor actor : Actor.all()) { + if (actor instanceof Char) { + passable[((Char)actor).pos] = false; + } + } + + int undeadsToSummon = maxArmySize() - Undead.count; + + PathFinder.buildDistanceMap( pos, passable, undeadsToSummon ); + PathFinder.distance[pos] = Integer.MAX_VALUE; + int dist = 1; + + undeadLabel: + for (int i=0; i < undeadsToSummon; i++) { + do { + for (int j=0; j < Level.LENGTH; j++) { + if (PathFinder.distance[j] == dist) { + + Undead undead = new Undead(); + undead.pos = j; + GameScene.add( undead ); + + WandOfBlink.appear( undead, j ); + new Flare( 3, 32 ).color( 0x000000, false ).show( undead.sprite, 2f ) ; + + PathFinder.distance[j] = Integer.MAX_VALUE; + + continue undeadLabel; + } + } + dist++; + } while (dist < undeadsToSummon); + } + + yell( "Arise, slaves!" ); + } + + @Override + public void notice() { + super.notice(); + yell( "How dare you!" ); + } + + @Override + public String description() { + return + "The last king of dwarves was known for his deep understanding of processes of life and death. " + + "He has persuaded members of his court to participate in a ritual, that should have granted them " + + "eternal youthfulness. In the end he was the only one, who got it - and an army of undead " + + "as a bonus."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( ToxicGas.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + RESISTANCES.add( WandOfDisintegration.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Paralysis.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + + public static class Undead extends Mob { + + public static int count = 0; + + { + name = "undead dwarf"; + spriteClass = UndeadSprite.class; + + HP = HT = 28; + defenseSkill = 15; + + EXP = 0; + + state = State.WANDERING; + } + + @Override + protected void onAdd() { + count++; + super.onAdd(); + } + + @Override + protected void onRemove() { + count--; + super.onRemove(); + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 12, 16 ); + } + + @Override + public int attackSkill( Char target ) { + return 16; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( MAX_ARMY_SIZE ) == 0) { + Buff.prolong( enemy, Paralysis.class, 1 ); + } + + return damage; + } + + @Override + public void damage( int dmg, Object src ) { + super.damage( dmg, src ); + if (src instanceof ToxicGas) { + ((ToxicGas)src).clear( pos ); + } + } + + @Override + public void die( Object cause ) { + super.die( cause ); + + if (Dungeon.visible[pos]) { + Sample.INSTANCE.play( Assets.SND_BONES ); + } + } + + @Override + public int dr() { + return 5; + } + + @Override + public String defenseVerb() { + return "blocked"; + } + + @Override + public String description() { + return + "These undead dwarves, risen by the will of the King of Dwarves, were members of his court. " + + "They appear as skeletons with a stunning amount of facial hair."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Death.class ); + IMMUNITIES.add( Paralysis.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Mob.java b/src/com/watabou/pixeldungeon/actors/mobs/Mob.java new file mode 100644 index 0000000000..e42b6c1b65 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Mob.java @@ -0,0 +1,528 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Sleep; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.effects.Wound; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public abstract class Mob extends Char { + + private static final String TXT_DIED = "You hear something died in the distance"; + + protected static final String TXT_NOTICE1 = "?!"; + protected static final String TXT_RAGE = "#$%^"; + protected static final String TXT_EXP = "%+dEXP"; + + public enum State { + SLEEPING, + HUNTING, + WANDERING, + FLEEING, + PASSIVE + } + public State state = State.SLEEPING; + + public Class spriteClass; + + protected int target = -1; + + protected int defenseSkill = 0; + + protected int EXP = 1; + protected int maxLvl = 30; + + protected Char enemy; + protected boolean enemySeen; + protected boolean alerted = false; + + protected static final float TIME_TO_WAKE_UP = 1f; + + public boolean hostile = true; + + // Unreachable target + public static final Mob DUMMY = new Mob() { + { + pos = -1; + } + }; + + private static final String STATE = "state"; + private static final String TARGET = "target"; + + @Override + public void storeInBundle( Bundle bundle ) { + + super.storeInBundle( bundle ); + + bundle.put( STATE, state.toString() ); + if (state != State.SLEEPING) { + bundle.put( TARGET, target ); + } + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + + super.restoreFromBundle( bundle ); + + state = State.valueOf( bundle.getString( STATE ) ); + if (state != State.SLEEPING) { + target = bundle.getInt( TARGET ); + } + } + + public CharSprite sprite() { + CharSprite sprite = null; + try { + sprite = spriteClass.newInstance(); + } catch (Exception e) { + } + return sprite; + } + + @Override + protected boolean act() { + + super.act(); + + boolean alertedNow = alerted; + alerted = false; + + sprite.hideAlert(); + + if (paralysed) { + enemySeen = false; + spend( TICK ); + return true; + } + + enemy = chooseEnemy(); + + boolean enemyInFOV = enemy.isAlive() && Level.fieldOfView[enemy.pos] && enemy.invisible <= 0; + + int oldPos = pos; + + switch (state) { + + case SLEEPING: + if (enemyInFOV && + Random.Int( distance( enemy ) + enemy.stealth() + (enemy.flying ? 2 : 0) ) == 0) { + + enemySeen = true; + + notice(); + state = State.HUNTING; + target = enemy.pos; + + spend( TIME_TO_WAKE_UP ); + + } else { + + enemySeen = false; + + spend( TICK ); + + } + return true; + + case WANDERING: + if (enemyInFOV && (alertedNow || Random.Int( distance( enemy ) / 2 + enemy.stealth() ) == 0)) { + + enemySeen = true; + + notice(); + state = State.HUNTING; + target = enemy.pos; + + } else { + + enemySeen = false; + + if (target != -1 && getCloser( target )) { + spend( 1 / speed() ); + return moveSprite( oldPos, pos ); + } else { + target = Dungeon.level.randomDestination(); + spend( TICK ); + } + + } + return true; + + case HUNTING: + enemySeen = enemyInFOV; + if (enemyInFOV && canAttack( enemy )) { + + return doAttack( enemy ); + + } else { + + if (enemyInFOV) { + target = enemy.pos; + } + + if (target != -1 && getCloser( target )) { + + + spend( 1 / speed() ); + return moveSprite( oldPos, pos ); + + } else { + + spend( TICK ); + state = State.WANDERING; + target = Dungeon.level.randomDestination(); // <-------- + return true; + } + } + + case FLEEING: + enemySeen = enemyInFOV; + if (enemyInFOV) { + target = enemy.pos; + } + if (target != -1 && getFurther( target )) { + + spend( 1 / speed() ); + return moveSprite( oldPos, pos ); + + } else { + + spend( TICK ); + nowhereToRun(); + + return true; + } + + case PASSIVE: + enemySeen = false; + spend( TICK ); + return true; + + } + + return true; + + } + + protected Char chooseEnemy() { + + if (buff( Amok.class ) != null) { + if (enemy == Dungeon.hero || enemy == null) { + + HashSet enemies = new HashSet(); + for (Mob mob:Dungeon.level.mobs) { + if (mob != this && Level.fieldOfView[mob.pos]) { + enemies.add( mob ); + } + } + if (enemies.size() > 0) { + return Random.element( enemies ); + } + + } else { + return enemy; + } + } + + Terror terror = (Terror)buff( Terror.class ); + if (terror != null) { + return terror.source; + } + + return Dungeon.hero; + } + + protected void nowhereToRun() { + } + + protected boolean moveSprite( int from, int to ) { + + if (sprite.isVisible() && (Dungeon.visible[from] || Dungeon.visible[to])) { + sprite.move( from, to ); + return false; + } else { + sprite.place( to ); + return true; + } + } + + @Override + public void add( Buff buff ) { + super.add( buff ); + if (buff instanceof Amok) { + if (sprite != null) { + sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE ); + } + state = State.HUNTING; + } else if (buff instanceof Terror) { + state = State.FLEEING; + } else if (buff instanceof Sleep) { + if (sprite != null) { + new Flare( 4, 32 ).color( 0x44ffff, true ).show( sprite, 2f ) ; + } + state = State.SLEEPING; + postpone( Sleep.SWS ); + } + } + + @Override + public void remove( Buff buff ) { + super.remove( buff ); + if (buff instanceof Terror) { + sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE ); + state = State.HUNTING; + } + } + + protected boolean canAttack( Char enemy ) { + return Level.adjacent( pos, enemy.pos ) && !pacified; + } + + protected boolean getCloser( int target ) { + + if (rooted) { + return false; + } + + int step = Dungeon.findPath( this, pos, target, + Level.passable, + Level.fieldOfView ); + if (step != -1) { + move( step ); + return true; + } else { + return false; + } + } + + protected boolean getFurther( int target ) { + int step = Dungeon.flee( this, pos, target, + Level.passable, + Level.fieldOfView ); + if (step != -1) { + move( step ); + return true; + } else { + return false; + } + } + + @Override + public void move( int step ) { + super.move( step ); + + if (!flying) { + Dungeon.level.mobPress( this ); + } + } + + protected float attackDelay() { + return 1f; + } + + protected boolean doAttack( Char enemy ) { + + boolean visible = Dungeon.visible[pos]; + + if (visible) { + sprite.attack( enemy.pos ); + } else { + attack( enemy ); + } + + spend( attackDelay() ); + + return !visible; + } + + @Override + public void onAttackComplete() { + attack( enemy ); + super.onAttackComplete(); + } + + @Override + public int defenseSkill( Char enemy ) { + return enemySeen && !paralysed ? defenseSkill : 0; + } + + @Override + public int defenseProc( Char enemy, int damage ) { + if (!enemySeen && enemy == Dungeon.hero && ((Hero)enemy).subClass == HeroSubClass.ASSASSIN) { + damage += Random.Int( 1, damage ); + Wound.hit( this ); + } + return damage; + } + + @Override + public void damage( int dmg, Object src ) { + + Terror.recover( this ); + + if (state == State.SLEEPING) { + state = State.WANDERING; + } + alerted = true; + + super.damage( dmg, src ); + } + + + @Override + public void destroy() { + + super.destroy(); + + Dungeon.level.mobs.remove( this ); + + if (Dungeon.hero.isAlive()) { + + if (hostile) { + Statistics.enemiesSlain++; + Badges.validateMonstersSlain(); + Statistics.qualifiedForNoKilling = false; + + if (Dungeon.nightMode) { + Statistics.nightHunt++; + } else { + Statistics.nightHunt = 0; + } + Badges.validateNightHunter(); + } + + if (Dungeon.hero.lvl <= maxLvl && EXP > 0) { + Dungeon.hero.sprite.showStatus( CharSprite.POSITIVE, TXT_EXP, EXP ); + Dungeon.hero.earnExp( EXP ); + } + } + } + + @Override + public void die( Object cause ) { + + super.die( cause ); + + if (Dungeon.hero.lvl <= maxLvl + 2) { + dropLoot(); + } + + if (Dungeon.hero.isAlive() && !Dungeon.visible[pos]) { + GLog.i( TXT_DIED ); + } + } + + protected Object loot = null; + protected float lootChance = 0; + + @SuppressWarnings("unchecked") + protected void dropLoot() { + if (loot != null && Random.Float() < lootChance) { + Item item = null; + if (loot instanceof Generator.Category) { + + item = Generator.random( (Generator.Category)loot ); + + } else if (loot instanceof Class) { + + item = Generator.random( (Class)loot ); + + } else { + + item = (Item)loot; + + } + Dungeon.level.drop( item, pos ).sprite.drop(); + } + } + + public boolean reset() { + return false; + } + + public void beckon( int cell ) { + + notice(); + + if (state != State.HUNTING) { + state = State.WANDERING; + } + target = cell; + } + + public String description() { + return "Real description is coming soon!"; + } + + public void notice() { + sprite.showAlert(); + } + + public void yell( String str ) { + GLog.n( "%s: \"%s\" ", name, str ); + } + + public static abstract class NPC extends Mob { + + { + HP = HT = 1; + EXP = 0; + + hostile = false; + state = State.PASSIVE; + } + + protected void throwItem() { + Heap heap = Dungeon.level.heaps.get( pos ); + if (heap != null) { + int n; + do { + n = pos + Level.NEIGHBOURS8[Random.Int( 8 )]; + } while (!Level.passable[n] && !Level.avoid[n]); + Dungeon.level.drop( heap.pickUp(), n ).sprite.drop( pos ); + } + } + + @Override + public void beckon( int cell ) { + } + + abstract public void interact(); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Monk.java b/src/com/watabou/pixeldungeon/actors/mobs/Monk.java new file mode 100644 index 0000000000..f3697acafd --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Monk.java @@ -0,0 +1,120 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.items.KindOfWeapon; +import com.watabou.pixeldungeon.items.food.Food; +import com.watabou.pixeldungeon.items.weapon.melee.Knuckles; +import com.watabou.pixeldungeon.sprites.MonkSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Random; + +public class Monk extends Mob { + + public static final String TXT_DISARM = "%s has knocked the %s from your hands!"; + + { + name = "dwarf monk"; + spriteClass = MonkSprite.class; + + HP = HT = 70; + defenseSkill = 30; + + EXP = 11; + maxLvl = 21; + + loot = new Food(); + lootChance = 0.083f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 12, 16 ); + } + + @Override + public int attackSkill( Char target ) { + return 30; + } + + @Override + protected float attackDelay() { + return 0.5f; + } + + @Override + public int dr() { + return 2; + } + + @Override + public String defenseVerb() { + return "parried"; + } + + @Override + public void die( Object cause ) { + Imp.Quest.process( this ); + + super.die( cause ); + } + + @Override + public int attackProc( Char enemy, int damage ) { + + if (Random.Int( 6 ) == 0 && enemy == Dungeon.hero) { + + Hero hero = Dungeon.hero; + KindOfWeapon weapon = hero.belongings.weapon; + + if (weapon != null && !(weapon instanceof Knuckles) && !weapon.cursed) { + hero.belongings.weapon = null; + Dungeon.level.drop( weapon, hero.pos ).sprite.drop(); + GLog.w( TXT_DISARM, name, weapon.name() ); + } + } + + return damage; + } + + @Override + public String description() { + return + "These monks are fanatics, who devoted themselves to protecting their city's secrets from all aliens. " + + "They don't use any armor or weapons, relying solely on the art of hand-to-hand combat."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Amok.class ); + IMMUNITIES.add( Terror.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Piranha.java b/src/com/watabou/pixeldungeon/actors/mobs/Piranha.java new file mode 100644 index 0000000000..551f5be1b6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Piranha.java @@ -0,0 +1,144 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Frost; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.PiranhaSprite; +import com.watabou.utils.Random; + +public class Piranha extends Mob { + + { + name = "giant piranha"; + spriteClass = PiranhaSprite.class; + + baseSpeed = 2f; + + EXP = 0; + } + + public Piranha() { + super(); + + HP = HT = 10 + Dungeon.depth * 5; + defenseSkill = 10 + Dungeon.depth * 2; + } + + @Override + protected boolean act() { + if (!Level.water[pos]) { + die( null ); + return true; + } else { + return super.act(); + } + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( Dungeon.depth, 4 + Dungeon.depth * 2 ); + } + + @Override + public int attackSkill( Char target ) { + return 20 + Dungeon.depth * 2; + } + + @Override + public int dr() { + return Dungeon.depth; + } + + @Override + public void die( Object cause ) { + Dungeon.level.drop( new MysteryMeat(), pos ).sprite.drop(); + super.die( cause ); + + Statistics.piranhasKilled++; + Badges.validatePiranhasKilled(); + } + + @Override + public boolean reset() { + return true; + } + + @Override + protected boolean getCloser( int target ) { + + if (rooted) { + return false; + } + + int step = Dungeon.findPath( this, pos, target, + Level.water, + Level.fieldOfView ); + if (step != -1) { + move( step ); + return true; + } else { + return false; + } + } + + @Override + protected boolean getFurther( int target ) { + int step = Dungeon.flee( this, pos, target, + Level.water, + Level.fieldOfView ); + if (step != -1) { + move( step ); + return true; + } else { + return false; + } + } + + @Override + public String description() { + return + "These carnivorous fish are not natural inhabitants of underground pools. " + + "They were bred specifically to protect flooded treasure vaults."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Burning.class ); + IMMUNITIES.add( Paralysis.class ); + IMMUNITIES.add( ToxicGas.class ); + IMMUNITIES.add( Roots.class ); + IMMUNITIES.add( Frost.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Rat.java b/src/com/watabou/pixeldungeon/actors/mobs/Rat.java new file mode 100644 index 0000000000..cb06ea288a --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Rat.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.npcs.Ghost; +import com.watabou.pixeldungeon.sprites.RatSprite; +import com.watabou.utils.Random; + +public class Rat extends Mob { + + { + name = "marsupial rat"; + spriteClass = RatSprite.class; + + HP = HT = 8; + defenseSkill = 3; + + maxLvl = 5; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 1, 5 ); + } + + @Override + public int attackSkill( Char target ) { + return 8; + } + + @Override + public int dr() { + return 1; + } + + @Override + public void die( Object cause ) { + Ghost.Quest.process( pos ); + + super.die( cause ); + } + + @Override + public String description() { + return + "Marsupial rats are aggressive, but rather weak denizens " + + "of the sewers. They can be dangerous only in big numbers."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Scorpio.java b/src/com/watabou/pixeldungeon/actors/mobs/Scorpio.java new file mode 100644 index 0000000000..724a43f71d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Scorpio.java @@ -0,0 +1,117 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.items.potions.PotionOfHealing; +import com.watabou.pixeldungeon.items.weapon.enchantments.Leech; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.sprites.ScorpioSprite; +import com.watabou.utils.Random; + +public class Scorpio extends Mob { + + { + name = "scorpio"; + spriteClass = ScorpioSprite.class; + + HP = HT = 95; + defenseSkill = 24; + viewDistance = Light.DISTANCE; + + EXP = 14; + maxLvl = 25; + + loot = new PotionOfHealing(); + lootChance = 0.125f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 20, 32 ); + } + + @Override + public int attackSkill( Char target ) { + return 36; + } + + @Override + public int dr() { + return 16; + } + + @Override + protected boolean canAttack( Char enemy ) { + return !Level.adjacent( pos, enemy.pos ) && Ballistica.cast( pos, enemy.pos, false, true ) == enemy.pos; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 2 ) == 0) { + Buff.prolong( enemy, Cripple.class, Cripple.DURATION ); + } + + return damage; + } + + @Override + protected boolean getCloser( int target ) { + if (state == State.HUNTING) { + return enemySeen && getFurther( target ); + } else { + return super.getCloser( target ); + } + } + + @Override + protected void dropLoot() { + if (Random.Int( 8 ) == 0) { + Dungeon.level.drop( new PotionOfHealing(), pos ).sprite.drop(); + } else if (Random.Int( 6 ) == 0) { + Dungeon.level.drop( new MysteryMeat(), pos ).sprite.drop(); + } + } + + @Override + public String description() { + return + "These huge arachnid-like demonic creatures avoid close combat by all means, " + + "firing crippling serrated spikes from long distances."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( Leech.class ); + RESISTANCES.add( Poison.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Senior.java b/src/com/watabou/pixeldungeon/actors/mobs/Senior.java new file mode 100644 index 0000000000..4f1e2cc113 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Senior.java @@ -0,0 +1,52 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.sprites.SeniorSprite; +import com.watabou.utils.Random; + +public class Senior extends Monk { + + { + name = "senior monk"; + spriteClass = SeniorSprite.class; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 12, 20 ); + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 10 ) == 0) { + Buff.prolong( enemy, Paralysis.class, 1.1f ); + } + return super.attackProc( enemy, damage ); + } + + @Override + public void die( Object cause ) { + super.die( cause ); + Badges.validateRare( this ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Shaman.java b/src/com/watabou/pixeldungeon/actors/mobs/Shaman.java new file mode 100644 index 0000000000..97515f9990 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Shaman.java @@ -0,0 +1,144 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.particles.SparkParticle; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.traps.LightningTrap; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ShamanSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public class Shaman extends Mob implements Callback { + + private static final float TIME_TO_ZAP = 2f; + + private static final String TXT_LIGHTNING_KILLED = "%s's lightning bolt killed you..."; + + { + name = "gnoll shaman"; + spriteClass = ShamanSprite.class; + + HP = HT = 18; + defenseSkill = 8; + + EXP = 6; + maxLvl = 14; + + loot = Generator.Category.SCROLL; + lootChance = 0.33f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 2, 6 ); + } + + @Override + public int attackSkill( Char target ) { + return 11; + } + + @Override + public int dr() { + return 4; + } + + @Override + protected boolean canAttack( Char enemy ) { + return Ballistica.cast( pos, enemy.pos, false, true ) == enemy.pos; + } + + @Override + protected boolean doAttack( Char enemy ) { + + if (Level.distance( pos, enemy.pos ) <= 1) { + + return super.doAttack( enemy ); + + } else { + + boolean visible = Level.fieldOfView[pos] || Level.fieldOfView[enemy.pos]; + if (visible) { + ((ShamanSprite)sprite).zap( enemy.pos ); + } + + spend( TIME_TO_ZAP ); + + if (hit( this, enemy, true )) { + int dmg = Random.Int( 2, 12 ); + if (Level.water[enemy.pos] && !enemy.flying) { + dmg *= 1.5f; + } + enemy.damage( dmg, LightningTrap.LIGHTNING ); + + enemy.sprite.centerEmitter().burst( SparkParticle.FACTORY, 3 ); + enemy.sprite.flash(); + + if (enemy == Dungeon.hero) { + + Camera.main.shake( 2, 0.3f ); + + if (!enemy.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.MOB, + Utils.indefinite( name ), Dungeon.depth ) ); + GLog.n( TXT_LIGHTNING_KILLED, name ); + } + } + } else { + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + } + + return !visible; + } + } + + @Override + public void call() { + next(); + } + + @Override + public String description() { + return + "The most intelligent gnolls can master shamanistic magic. Gnoll shamans prefer " + + "battle spells to compensate for lack of might, not hesitating to use them " + + "on those who question their status in a tribe."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( LightningTrap.Electricity.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Shielded.java b/src/com/watabou/pixeldungeon/actors/mobs/Shielded.java new file mode 100644 index 0000000000..c4e4329d09 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Shielded.java @@ -0,0 +1,47 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.sprites.ShieldedSprite; + +public class Shielded extends Brute { + + { + name = "shielded brute"; + spriteClass = ShieldedSprite.class; + + defenseSkill = 20; + } + + @Override + public int dr() { + return 10; + } + + @Override + public String defenseVerb() { + return "blocked"; + } + + @Override + public void die( Object cause ) { + super.die( cause ); + Badges.validateRare( this ); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Skeleton.java b/src/com/watabou/pixeldungeon/actors/mobs/Skeleton.java new file mode 100644 index 0000000000..4c9b3a3582 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Skeleton.java @@ -0,0 +1,129 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.SkeletonSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Random; + +public class Skeleton extends Mob { + + private static final String TXT_HERO_KILLED = "You were killed by the explosion of bones..."; + + { + name = "skeleton"; + spriteClass = SkeletonSprite.class; + + HP = HT = 25; + defenseSkill = 9; + + EXP = 5; + maxLvl = 10; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 3, 8 ); + } + + @Override + public void die( Object cause ) { + + super.die( cause ); + + boolean heroKilled = false; + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + Char ch = findChar( pos + Level.NEIGHBOURS8[i] ); + if (ch != null && ch.isAlive()) { + int damage = Math.max( 0, damageRoll() - Random.IntRange( 0, ch.dr() / 2 ) ); + ch.damage( damage, this ); + if (ch == Dungeon.hero && !ch.isAlive()) { + heroKilled = true; + } + } + } + + if (Dungeon.visible[pos]) { + Sample.INSTANCE.play( Assets.SND_BONES ); + } + + if (heroKilled) { + Dungeon.fail( Utils.format( ResultDescriptions.MOB, Utils.indefinite( name ), Dungeon.depth ) ); + GLog.n( TXT_HERO_KILLED ); + } + } + + @Override + protected void dropLoot() { + if (Random.Int( 5 ) == 0) { + Item loot = Generator.random( Generator.Category.WEAPON ); + for (int i=0; i < 2; i++) { + Item l = Generator.random( Generator.Category.WEAPON ); + if (l.level < loot.level) { + loot = l; + } + } + Dungeon.level.drop( loot, pos ).sprite.drop(); + } + } + + @Override + public int attackSkill( Char target ) { + return 12; + } + + @Override + public int dr() { + return 5; + } + + @Override + public String defenseVerb() { + return "blocked"; + } + + @Override + public String description() { + return + "Skeletons are composed of corpses bones from unlucky adventurers and inhabitants of the dungeon, " + + "animated by emanations of evil magic from the depths below. After they have been " + + "damaged enough, they disintegrate in an explosion of bones."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Death.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Spinner.java b/src/com/watabou/pixeldungeon/actors/mobs/Spinner.java new file mode 100644 index 0000000000..c93e73dbcf --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Spinner.java @@ -0,0 +1,130 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Web; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.SpinnerSprite; +import com.watabou.utils.Random; + +public class Spinner extends Mob { + + { + name = "cave spinner"; + spriteClass = SpinnerSprite.class; + + HP = HT = 50; + defenseSkill = 14; + + EXP = 9; + maxLvl = 16; + + loot = new MysteryMeat(); + lootChance = 0.125f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 12, 16 ); + } + + @Override + protected void nowhereToRun() { + if (buff( Terror.class ) == null) { + state = State.HUNTING; + } else { + super.nowhereToRun(); + } + } + + @Override + public int attackSkill( Char target ) { + return 20; + } + + @Override + public int dr() { + return 6; + } + + @Override + protected boolean act() { + boolean result = super.act(); + + if (state == State.FLEEING && buff( Terror.class ) == null && + enemySeen && enemy.buff( Poison.class ) == null) { + + state = State.HUNTING; + } + return result; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 2 ) == 0) { + Buff.affect( enemy, Poison.class ).set( Random.Int( 5, 7 ) * Poison.durationFactor( enemy ) ); + state = State.FLEEING; + } + + return damage; + } + + @Override + public void move( int step ) { + if (state == State.FLEEING) { + GameScene.add( Blob.seed( pos, Random.Int( 5, 7 ), Web.class ) ); + } + super.move( step ); + } + + @Override + public String description() { + return + "These greenish furry cave spiders try to avoid direct combat, preferring to wait in the distance " + + "while their victim, entangled in the spinner's excreted cobweb, slowly dies from their poisonous bite."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( Poison.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Roots.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Statue.java b/src/com/watabou/pixeldungeon/actors/mobs/Statue.java new file mode 100644 index 0000000000..3e9ba24a3d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Statue.java @@ -0,0 +1,170 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.Weapon.Enchantment; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.items.weapon.enchantments.Leech; +import com.watabou.pixeldungeon.items.weapon.melee.MeleeWeapon; +import com.watabou.pixeldungeon.sprites.StatueSprite; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Statue extends Mob { + + { + name = "animated statue"; + spriteClass = StatueSprite.class; + + EXP = 0; + state = State.PASSIVE; + } + + private Weapon weapon; + + public Statue() { + super(); + + do { + weapon = (Weapon)Generator.random( Generator.Category.WEAPON ); + } while (!(weapon instanceof MeleeWeapon) || weapon.level < 0); + + weapon.identify(); + weapon.enchant( Enchantment.random() ); + + HP = HT = 15 + Dungeon.depth * 5; + defenseSkill = 4 + Dungeon.depth; + } + + private static final String WEAPON = "weapon"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( WEAPON, weapon ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + weapon = (Weapon)bundle.get( WEAPON ); + } + + @Override + protected boolean act() { + if (Dungeon.visible[pos]) { + Journal.add( Journal.Feature.STATUE ); + } + return super.act(); + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( weapon.MIN, weapon.MAX ); + } + + @Override + public int attackSkill( Char target ) { + return (int)((9 + Dungeon.depth) * weapon.ACU); + } + + @Override + protected float attackDelay() { + return weapon.DLY; + } + + @Override + public int dr() { + return Dungeon.depth; + } + + @Override + public void damage( int dmg, Object src ) { + + if (state == State.PASSIVE) { + state = State.HUNTING; + } + + super.damage( dmg, src ); + } + + @Override + public int attackProc( Char enemy, int damage ) { + weapon.proc( this, enemy, damage ); + return damage; + } + + @Override + public void beckon( int cell ) { + } + + @Override + public void die( Object cause ) { + Dungeon.level.drop( weapon, pos ).sprite.drop(); + super.die( cause ); + } + + @Override + public void destroy() { + Journal.remove( Journal.Feature.STATUE ); + super.destroy(); + } + + @Override + public boolean reset() { + state = State.PASSIVE; + return true; + } + + @Override + public String description() { + return + "You would think that it's just another ugly statue of this dungeon, but its red glowing eyes give itself away. " + + "While the statue itself is made of stone, the _" + weapon.name() + "_, it's wielding, looks real."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + RESISTANCES.add( ToxicGas.class ); + RESISTANCES.add( Poison.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + IMMUNITIES.add( Leech.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Succubus.java b/src/com/watabou/pixeldungeon/actors/mobs/Succubus.java new file mode 100644 index 0000000000..764c593241 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Succubus.java @@ -0,0 +1,142 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Charm; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.buffs.Sleep; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfLullaby; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.items.weapon.enchantments.Leech; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.sprites.SuccubusSprite; +import com.watabou.utils.Random; + +public class Succubus extends Mob { + + private static final int BLINK_DELAY = 5; + + private int delay = 0; + + { + name = "succubus"; + spriteClass = SuccubusSprite.class; + + HP = HT = 80; + defenseSkill = 25; + viewDistance = Light.DISTANCE; + + EXP = 12; + maxLvl = 25; + + loot = new ScrollOfLullaby(); + lootChance = 0.05f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 15, 25 ); + } + + @Override + public int attackProc( Char enemy, int damage ) { + + if (Random.Int( 3 ) == 0) { + Buff.affect( enemy, Charm.class, Charm.durationFactor( enemy ) * Random.IntRange( 2, 5 ) ); + enemy.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 ); + Sample.INSTANCE.play( Assets.SND_CHARMS ); + } + + return damage; + } + + @Override + protected boolean getCloser( int target ) { + if (Level.fieldOfView[target] && Level.distance( pos, target ) > 2 && delay <= 0) { + + blink( target ); + spend( -1 / speed() ); + return true; + + } else { + + delay--; + return super.getCloser( target ); + + } + } + + private void blink( int target ) { + + int cell = Ballistica.cast( pos, target, true, true ); + + if (Actor.findChar( cell ) != null && Ballistica.distance > 1) { + cell = Ballistica.trace[Ballistica.distance - 2]; + } + + WandOfBlink.appear( this, cell ); + + delay = BLINK_DELAY; + } + + @Override + public int attackSkill( Char target ) { + return 40; + } + + @Override + public int dr() { + return 10; + } + + @Override + public String description() { + return + "The succubi are demons that look like seductive (in a slightly gothic way) girls. Using its magic, the succubus " + + "can charm a hero, who will become unable to attack anything until the charm wears off."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( Leech.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Sleep.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Swarm.java b/src/com/watabou/pixeldungeon/actors/mobs/Swarm.java new file mode 100644 index 0000000000..9e3dcb54b0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Swarm.java @@ -0,0 +1,145 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.effects.Pushing; +import com.watabou.pixeldungeon.items.potions.PotionOfHealing; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.levels.features.Door; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.SwarmSprite; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Swarm extends Mob { + + { + name = "swarm of flies"; + spriteClass = SwarmSprite.class; + + HP = HT = 80; + defenseSkill = 5; + + maxLvl = 10; + + flying = true; + } + + private static final float SPLIT_DELAY = 1f; + + int generation = 0; + + private static final String GENERATION = "generation"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( GENERATION, generation ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + generation = bundle.getInt( GENERATION ); + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 1, 4 ); + } + + @Override + public int defenseProc( Char enemy, int damage ) { + + if (HP >= damage + 2) { + ArrayList candidates = new ArrayList(); + boolean[] passable = Level.passable; + + int[] neighbours = {pos + 1, pos - 1, pos + Level.WIDTH, pos - Level.WIDTH}; + for (int n : neighbours) { + if (passable[n] && Actor.findChar( n ) == null) { + candidates.add( n ); + } + } + + if (candidates.size() > 0) { + + Swarm clone = split(); + clone.HP = (HP - damage) / 2; + clone.pos = Random.element( candidates ); + clone.state = State.HUNTING; + + if (Dungeon.level.map[clone.pos] == Terrain.DOOR) { + Door.enter( clone.pos ); + } + + GameScene.add( clone, SPLIT_DELAY ); + Actor.addDelayed( new Pushing( clone, pos, clone.pos ), -1 ); + + HP -= clone.HP; + } + } + + return damage; + } + + @Override + public int attackSkill( Char target ) { + return 12; + } + + @Override + public String defenseVerb() { + return "evaded"; + } + + private Swarm split() { + Swarm clone = new Swarm(); + clone.generation = generation + 1; + if (buff( Burning.class ) != null) { + Buff.affect( clone, Burning.class ).reignite( clone ); + } + if (buff( Poison.class ) != null) { + Buff.affect( clone, Poison.class ).set( 2 ); + } + return clone; + } + + @Override + protected void dropLoot() { + if (Random.Int( 5 * (generation + 1) ) == 0) { + Dungeon.level.drop( new PotionOfHealing(), pos ).sprite.drop(); + } + } + + @Override + public String description() { + return + "The deadly swarm of flies buzzes angrily. Every non-magical attack " + + "will split it into two smaller but equally dangerous swarms."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Tengu.java b/src/com/watabou/pixeldungeon/actors/mobs/Tengu.java new file mode 100644 index 0000000000..be59824ca4 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Tengu.java @@ -0,0 +1,193 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Badges.Badge; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.TomeOfMastery; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfMagicMapping; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.TenguSprite; +import com.watabou.utils.Random; + +public class Tengu extends Mob { + + private static final int JUMP_DELAY = 5; + + { + name = "Tengu"; + spriteClass = TenguSprite.class; + + HP = HT = 120; + EXP = 20; + defenseSkill = 20; + } + + private int timeToJump = JUMP_DELAY; + + @Override + public int damageRoll() { + return Random.NormalIntRange( 8, 15 ); + } + + @Override + public int attackSkill( Char target ) { + return 20; + } + + @Override + public int dr() { + return 5; + } + + @Override + public void die( Object cause ) { + + Badges.Badge badgeToCheck = null; + switch (Dungeon.hero.heroClass) { + case WARRIOR: + badgeToCheck = Badge.MASTERY_WARRIOR; + break; + case MAGE: + badgeToCheck = Badge.MASTERY_MAGE; + break; + case ROGUE: + badgeToCheck = Badge.MASTERY_ROGUE; + break; + case HUNTRESS: + badgeToCheck = Badge.MASTERY_HUNTRESS; + break; + } + if (!Badges.isUnlocked( badgeToCheck )) { + Dungeon.level.drop( new TomeOfMastery(), pos ).sprite.drop(); + } + + GameScene.bossSlain(); + Dungeon.level.drop( new SkeletonKey( Dungeon.depth ), pos ).sprite.drop(); + super.die( cause ); + + Badges.validateBossSlain(); + + yell( "Free at last..." ); + } + + @Override + protected boolean getCloser( int target ) { + if (Level.fieldOfView[target]) { + jump(); + return true; + } else { + return super.getCloser( target ); + } + } + + @Override + protected boolean canAttack( Char enemy ) { + return Ballistica.cast( pos, enemy.pos, false, true ) == enemy.pos; + } + + @Override + protected boolean doAttack( Char enemy ) { + timeToJump--; + if (timeToJump <= 0 && Level.adjacent( pos, enemy.pos )) { + jump(); + return true; + } else { + return super.doAttack( enemy ); + } + } + + private void jump() { + timeToJump = JUMP_DELAY; + + for (int i=0; i < 4; i++) { + int trapPos; + do { + trapPos = Random.Int( Level.LENGTH ); + } while (!Level.fieldOfView[trapPos] || !Level.passable[trapPos]); + + if (Dungeon.level.map[trapPos] == Terrain.INACTIVE_TRAP) { + Level.set( trapPos, Terrain.POISON_TRAP ); + GameScene.updateMap( trapPos ); + ScrollOfMagicMapping.discover( trapPos ); + } + } + + int newPos; + do { + newPos = Random.Int( Level.LENGTH ); + } while ( + !Level.fieldOfView[newPos] || + !Level.passable[newPos] || + Level.adjacent( newPos, enemy.pos ) || + Actor.findChar( newPos ) != null); + + sprite.move( pos, newPos ); + move( newPos ); + + if (Dungeon.visible[newPos]) { + CellEmitter.get( newPos ).burst( Speck.factory( Speck.WOOL ), 6 ); + Sample.INSTANCE.play( Assets.SND_PUFF ); + } + + spend( 1 / speed() ); + } + + @Override + public void notice() { + super.notice(); + yell( "Gotcha, " + Dungeon.hero.heroClass.title() + "!" ); + } + + @Override + public String description() { + return + "Tengu are members of the ancient assassins clan, which is also called Tengu. " + + "These assassins are noted for extensive use of shuriken and traps."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( ToxicGas.class ); + RESISTANCES.add( Poison.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Thief.java b/src/com/watabou/pixeldungeon/actors/mobs/Thief.java new file mode 100644 index 0000000000..d73f2e636d --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Thief.java @@ -0,0 +1,156 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.rings.RingOfHaggler; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ThiefSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Thief extends Mob { + + protected static final String TXT_STOLE = "%s stole %s from you!"; + protected static final String TXT_CARRIES = "\n\n%s is carrying a _%s_. Stolen obviously."; + + public Item item; + + { + name = "crazy thief"; + spriteClass = ThiefSprite.class; + + HP = HT = 20; + defenseSkill = 12; + + EXP = 5; + maxLvl = 10; + + loot = RingOfHaggler.class; + lootChance = 0.01f; + } + + private static final String ITEM = "item"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( ITEM, item ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + item = (Item)bundle.get( ITEM ); + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 1, 7 ); + } + + @Override + protected float attackDelay() { + return 0.5f; + } + + @Override + protected void nowhereToRun() { + if (buff( Terror.class ) == null) { + sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE ); + state = State.HUNTING; + } else { + super.nowhereToRun(); + } + } + + @Override + public void die( Object cause ) { + + super.die( cause ); + + if (item != null) { + Dungeon.level.drop( item, pos ).sprite.drop(); + } + } + + @Override + public int attackSkill( Char target ) { + return 12; + } + + @Override + public int dr() { + return 3; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (item == null && enemy instanceof Hero && steal( (Hero)enemy )) { + state = State.FLEEING; + } + + return damage; + } + + @Override + public int defenseProc(Char enemy, int damage) { + if (state == State.FLEEING) { + Dungeon.level.drop( new Gold(), pos ).sprite.drop(); + } + + return damage; + } + + protected boolean steal( Hero hero ) { + + Item item = hero.belongings.randomUnequipped(); + if (item != null) { + + GLog.w( TXT_STOLE, this.name, item.name() ); + + item.detachAll( hero.belongings.backpack ); + this.item = item; + + return true; + } else { + return false; + } + } + + @Override + public String description() { + String desc = + "Deeper levels of the dungeon have always been a hiding place for all kinds of criminals. " + + "Not all of them could keep a clear mind during their extended periods so far from daylight. Long ago, " + + "these crazy thieves and bandits have forgotten who they are and why they steal."; + + if (item != null) { + desc += String.format( TXT_CARRIES, Utils.capitalize( this.name ), item.name() ); + } + + return desc; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Warlock.java b/src/com/watabou/pixeldungeon/actors/mobs/Warlock.java new file mode 100644 index 0000000000..de3e863339 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Warlock.java @@ -0,0 +1,145 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Weakness; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.WarlockSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public class Warlock extends Mob implements Callback { + + private static final float TIME_TO_ZAP = 1f; + + private static final String TXT_SHADOWBOLT_KILLED = "%s's shadow bolt killed you..."; + + { + name = "dwarf warlock"; + spriteClass = WarlockSprite.class; + + HP = HT = 70; + defenseSkill = 18; + + EXP = 11; + maxLvl = 21; + + loot = Generator.Category.POTION; + lootChance = 0.83f; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 12, 20 ); + } + + @Override + public int attackSkill( Char target ) { + return 25; + } + + @Override + public int dr() { + return 8; + } + + @Override + protected boolean canAttack( Char enemy ) { + return Ballistica.cast( pos, enemy.pos, false, true ) == enemy.pos; + } + + protected boolean doAttack( Char enemy ) { + + if (Level.adjacent( pos, enemy.pos )) { + + return super.doAttack( enemy ); + + } else { + + boolean visible = Level.fieldOfView[pos] || Level.fieldOfView[enemy.pos]; + if (visible) { + ((WarlockSprite)sprite).zap( enemy.pos ); + } else { + zap(); + } + + return !visible; + } + } + + private void zap() { + spend( TIME_TO_ZAP ); + + if (hit( this, enemy, true )) { + if (enemy == Dungeon.hero && Random.Int( 2 ) == 0) { + Buff.prolong( enemy, Weakness.class, Weakness.duration( enemy ) ); + } + + int dmg = Random.Int( 12, 18 ); + enemy.damage( dmg, this ); + + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail( Utils.format( ResultDescriptions.MOB, + Utils.indefinite( name ), Dungeon.depth ) ); + GLog.n( TXT_SHADOWBOLT_KILLED, name ); + } + } else { + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + } + } + + public void onZapComplete() { + zap(); + next(); + } + + @Override + public void call() { + next(); + } + + @Override + public String description() { + return + "When dwarves' interests have shifted from engineering to arcane arts, " + + "warlocks have come to power in the city. They started with elemental magic, " + + "but soon switched to demonology and necromancy."; + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( Death.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Wraith.java b/src/com/watabou/pixeldungeon/actors/mobs/Wraith.java new file mode 100644 index 0000000000..2a05e26d0b --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Wraith.java @@ -0,0 +1,139 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.HashSet; + +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.WraithSprite; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Wraith extends Mob { + + private static final float SPAWN_DELAY = 2f; + + private int level; + + { + name = "wraith"; + spriteClass = WraithSprite.class; + + HP = HT = 1; + EXP = 0; + + flying = true; + } + + private static final String LEVEL = "level"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( LEVEL, level ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + level = bundle.getInt( LEVEL ); + adjustStats( level ); + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 1, 3 + level ); + } + + @Override + public int attackSkill( Char target ) { + return 10 + level; + } + + public void adjustStats( int level ) { + this.level = level; + defenseSkill = attackSkill( null ) * 5; + enemySeen = true; + } + + @Override + public String defenseVerb() { + return "evaded"; + } + + @Override + public boolean reset() { + state = State.WANDERING; + return true; + } + + @Override + public String description() { + return + "A wraith is a vengeful spirit of a sinner, whose grave or tomb was disturbed. " + + "Being an ethereal entity, it is very hard to hit with a regular weapon."; + } + + public static void spawnAround( int pos ) { + for (int n : Level.NEIGHBOURS4) { + int cell = pos + n; + if (Level.passable[cell] && Actor.findChar( cell ) == null) { + spawnAt( cell ); + } + } + } + + public static Wraith spawnAt( int pos ) { + if (Level.passable[pos] && Actor.findChar( pos ) == null) { + + Wraith w = new Wraith(); + w.adjustStats( Dungeon.depth ); + w.pos = pos; + w.state = State.HUNTING; + GameScene.add( w, SPAWN_DELAY ); + + w.sprite.alpha( 0 ); + w.sprite.parent.add( new AlphaTweener( w.sprite, 1, 0.5f ) ); + + w.sprite.emitter().burst( ShadowParticle.CURSE, 5 ); + + return w; + } else { + return null; + } + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Death.class ); + IMMUNITIES.add( Terror.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/Yog.java b/src/com/watabou/pixeldungeon/actors/mobs/Yog.java new file mode 100644 index 0000000000..e8a2dad05b --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/Yog.java @@ -0,0 +1,430 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs; + +import java.util.ArrayList; +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Charm; +import com.watabou.pixeldungeon.actors.buffs.Ooze; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Sleep; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.effects.Pushing; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfPsionicBlast; +import com.watabou.pixeldungeon.items.weapon.enchantments.Death; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.BurningFistSprite; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.LarvaSprite; +import com.watabou.pixeldungeon.sprites.RottingFistSprite; +import com.watabou.pixeldungeon.sprites.YogSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Random; + +public class Yog extends Mob { + + { + name = "Yog-Dzewa"; + spriteClass = YogSprite.class; + + HP = HT = 300; + + EXP = 50; + + state = State.PASSIVE; + } + + private static final String TXT_DESC = + "Yog-Dzewa is an Old God, a powerful entity from the realms of chaos. A century ago, the ancient dwarves " + + "barely won the war against its army of demons, but were unable to kill the god itself. Instead, they then " + + "imprisoned it in the halls below their city, believing it to be too weak to rise ever again."; + + private static int fistsCount = 0; + + public Yog() { + super(); + } + + public void spawnFists() { + RottingFist fist1 = new RottingFist(); + BurningFist fist2 = new BurningFist(); + + do { + fist1.pos = pos + Level.NEIGHBOURS8[Random.Int( 8 )]; + fist2.pos = pos + Level.NEIGHBOURS8[Random.Int( 8 )]; + } while (!Level.passable[fist1.pos] || !Level.passable[fist2.pos] || fist1.pos == fist2.pos); + + GameScene.add( fist1 ); + GameScene.add( fist2 ); + } + + @Override + public void damage( int dmg, Object src ) { + + if (fistsCount > 0) { + + for (Mob mob : Dungeon.level.mobs) { + if (mob instanceof BurningFist || mob instanceof RottingFist) { + mob.beckon( pos ); + } + } + + dmg >>= fistsCount; + } + + super.damage( dmg, src ); + } + + @Override + public int defenseProc( Char enemy, int damage ) { + + ArrayList spawnPoints = new ArrayList(); + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + int p = pos + Level.NEIGHBOURS8[i]; + if (Actor.findChar( p ) == null && (Level.passable[p] || Level.avoid[p])) { + spawnPoints.add( p ); + } + } + + if (spawnPoints.size() > 0) { + Larva larva = new Larva(); + larva.pos = Random.element( spawnPoints ); + + GameScene.add( larva ); + Actor.addDelayed( new Pushing( larva, pos, larva.pos ), -1 ); + } + + return super.defenseProc(enemy, damage); + } + + @Override + public void beckon( int cell ) { + } + + @SuppressWarnings("unchecked") + @Override + public void die( Object cause ) { + + for (Mob mob : (Iterable)Dungeon.level.mobs.clone()) { + if (mob instanceof BurningFist || mob instanceof RottingFist) { + mob.die( cause ); + } + } + + GameScene.bossSlain(); + Dungeon.level.drop( new SkeletonKey( Dungeon.depth ), pos ).sprite.drop(); + super.die( cause ); + + yell( "..." ); + } + + @Override + public void notice() { + super.notice(); + yell( "Hope is an illusion..." ); + } + + @Override + public String description() { + return TXT_DESC; + + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + + IMMUNITIES.add( Death.class ); + IMMUNITIES.add( Terror.class ); + IMMUNITIES.add( Amok.class ); + IMMUNITIES.add( Charm.class ); + IMMUNITIES.add( Sleep.class ); + IMMUNITIES.add( Burning.class ); + IMMUNITIES.add( ToxicGas.class ); + IMMUNITIES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + + public static class RottingFist extends Mob { + + private static final int REGENERATION = 4; + + { + name = "rotting fist"; + spriteClass = RottingFistSprite.class; + + HP = HT = 300; + defenseSkill = 25; + + EXP = 0; + + state = State.WANDERING; + } + + public RottingFist() { + super(); + fistsCount++; + } + + @Override + public void die( Object cause ) { + super.die( cause ); + fistsCount--; + } + + @Override + public int attackSkill( Char target ) { + return 36; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 24, 36 ); + } + + @Override + public int dr() { + return 15; + } + + @Override + public int attackProc( Char enemy, int damage ) { + if (Random.Int( 3 ) == 0) { + Buff.affect( enemy, Ooze.class ); + enemy.sprite.burst( 0xFF000000, 5 ); + } + + return damage; + } + + @Override + public boolean act() { + + if (Level.water[pos] && HP < HT) { + sprite.emitter().burst( ShadowParticle.UP, 2 ); + HP += REGENERATION; + } + + return super.act(); + } + + @Override + public String description() { + return TXT_DESC; + + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( ToxicGas.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Amok.class ); + IMMUNITIES.add( Sleep.class ); + IMMUNITIES.add( Terror.class ); + IMMUNITIES.add( Poison.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + } + + public static class BurningFist extends Mob { + + { + name = "burning fist"; + spriteClass = BurningFistSprite.class; + + HP = HT = 200; + defenseSkill = 25; + + EXP = 0; + + state = State.WANDERING; + } + + public BurningFist() { + super(); + fistsCount++; + } + + @Override + public void die( Object cause ) { + super.die( cause ); + fistsCount--; + } + + @Override + public int attackSkill( Char target ) { + return 36; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 20, 32 ); + } + + @Override + public int dr() { + return 15; + } + + @Override + protected boolean canAttack( Char enemy ) { + return Ballistica.cast( pos, enemy.pos, false, true ) == enemy.pos; + } + + @Override + public boolean attack( Char enemy ) { + + if (!Level.adjacent( pos, enemy.pos )) { + spend( attackDelay() ); + + if (hit( this, enemy, true )) { + + int dmg = damageRoll(); + enemy.damage( dmg, this ); + + enemy.sprite.bloodBurstA( sprite.center(), dmg ); + enemy.sprite.flash(); + + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail( Utils.format( ResultDescriptions.BOSS, name, Dungeon.depth ) ); + GLog.n( TXT_KILL, name ); + } + return true; + + } else { + + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + return false; + } + } else { + return super.attack( enemy ); + } + } + + @Override + public boolean act() { + + for (int i=0; i < Level.NEIGHBOURS9.length; i++) { + GameScene.add( Blob.seed( pos + Level.NEIGHBOURS9[i], 2, Fire.class ) ); + } + + return super.act(); + } + + @Override + public String description() { + return TXT_DESC; + + } + + private static final HashSet> RESISTANCES = new HashSet>(); + static { + RESISTANCES.add( ToxicGas.class ); + RESISTANCES.add( Death.class ); + RESISTANCES.add( ScrollOfPsionicBlast.class ); + } + + @Override + public HashSet> resistances() { + return RESISTANCES; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Amok.class ); + IMMUNITIES.add( Sleep.class ); + IMMUNITIES.add( Terror.class ); + IMMUNITIES.add( Burning.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + } + + public static class Larva extends Mob { + + { + name = "god's larva"; + spriteClass = LarvaSprite.class; + + HP = HT = 25; + defenseSkill = 20; + + EXP = 0; + + state = State.HUNTING; + } + + @Override + public int attackSkill( Char target ) { + return 30; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 15, 20 ); + } + + @Override + public int dr() { + return 8; + } + + @Override + public String description() { + return TXT_DESC; + + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/Blacksmith.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Blacksmith.java new file mode 100644 index 0000000000..d10529a14a --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Blacksmith.java @@ -0,0 +1,326 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import java.util.Collection; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.EquipableItem; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.quest.DarkGold; +import com.watabou.pixeldungeon.items.quest.Pickaxe; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.BlacksmithSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBlacksmith; +import com.watabou.pixeldungeon.windows.WndQuest; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Blacksmith extends Mob.NPC { + + private static final String TXT_GOLD_1 = + "Hey human! Wanna be useful, eh? Take dis pickaxe and mine me some _dark gold ore_, _15 pieces_ should be enough. " + + "What do you mean, how am I gonna pay? You greedy...\n" + + "Ok, ok, I don't have money to pay, but I can do some smithin' for you. Consider yourself lucky, " + + "I'm the only blacksmith around."; + private static final String TXT_BLOOD_1 = + "Hey human! Wanna be useful, eh? Take dis pickaxe and _kill a bat_ wit' it, I need its blood on the head. " + + "What do you mean, how am I gonna pay? You greedy...\n" + + "Ok, ok, I don't have money to pay, but I can do some smithin' for you. Consider yourself lucky, " + + "I'm the only blacksmith around."; + private static final String TXT2 = + "Are you kiddin' me? Where is my pickaxe?!"; + private static final String TXT3 = + "Dark gold ore. 15 pieces. Seriously, is it dat hard?"; + private static final String TXT4 = + "I said I need bat blood on the pickaxe. Chop chop!"; + private static final String TXT_COMPLETED = + "Oh, you have returned... Better late dan never."; + private static final String TXT_GET_LOST = + "I'm busy. Get lost!"; + + private static final String TXT_LOOKS_BETTER = "your %s certainly looks better now"; + + { + name = "troll blacksmith"; + spriteClass = BlacksmithSprite.class; + } + + @Override + protected boolean act() { + throwItem(); + return super.act(); + } + + @Override + public void interact() { + + sprite.turnTo( pos, Dungeon.hero.pos ); + + if (!Quest.given) { + + GameScene.show( new WndQuest( this, + Quest.alternative ? TXT_BLOOD_1 : TXT_GOLD_1 ) { + + @Override + public void onBackPressed() { + super.onBackPressed(); + + Quest.given = true; + Quest.completed = false; + + Pickaxe pick = new Pickaxe(); + if (pick.doPickUp( Dungeon.hero )) { + GLog.i( Hero.TXT_YOU_NOW_HAVE, pick.name() ); + } else { + Dungeon.level.drop( pick, Dungeon.hero.pos ).sprite.drop(); + } + }; + } ); + + Journal.add( Journal.Feature.TROLL ); + + } else if (!Quest.completed) { + if (Quest.alternative) { + + Pickaxe pick = Dungeon.hero.belongings.getItem( Pickaxe.class ); + if (pick == null) { + tell( TXT2 ); + } else if (!pick.bloodStained) { + tell( TXT4 ); + } else { + if (pick.isEquipped( Dungeon.hero )) { + pick.doUnequip( Dungeon.hero, false ); + } + pick.detach( Dungeon.hero.belongings.backpack ); + tell( TXT_COMPLETED ); + + Quest.completed = true; + Quest.reforged = false; + } + + } else { + + Pickaxe pick = Dungeon.hero.belongings.getItem( Pickaxe.class ); + DarkGold gold = Dungeon.hero.belongings.getItem( DarkGold.class ); + if (pick == null) { + tell( TXT2 ); + } else if (gold == null || gold.quantity() < 15) { + tell( TXT3 ); + } else { + if (pick.isEquipped( Dungeon.hero )) { + pick.doUnequip( Dungeon.hero, false ); + } + pick.detach( Dungeon.hero.belongings.backpack ); + gold.detachAll( Dungeon.hero.belongings.backpack ); + tell( TXT_COMPLETED ); + + Quest.completed = true; + Quest.reforged = false; + } + + } + } else if (!Quest.reforged) { + + GameScene.show( new WndBlacksmith( this, Dungeon.hero ) ); + + } else { + + tell( TXT_GET_LOST ); + + } + } + + private void tell( String text ) { + GameScene.show( new WndQuest( this, text ) ); + } + + public static String verify( Item item1, Item item2 ) { + + if (item1 == item2) { + return "Select 2 different items, not the same item twice!"; + } + + if (!item1.isSimilar( item2 )) { + return "Select 2 items of the same type!"; + } + + if (!item1.isIdentified() || !item2.isIdentified()) { + return "I need to know what I'm working with, identify them first!"; + } + + if (item1.cursed || item2.cursed) { + return "I don't work with cursed items!"; + } + + if (item1.level < 0 || item2.level < 0) { + return "It's a junk, the quality is too poor!"; + } + + if (!item1.isUpgradable() || !item2.isUpgradable()) { + return "I can't reforge these items!"; + } + + return null; + } + + public static void upgrade( Item item1, Item item2 ) { + + Item first, second; + if (item2.level > item1.level) { + first = item2; + second = item1; + } else { + first = item1; + second = item2; + } + + Sample.INSTANCE.play( Assets.SND_EVOKE ); + ScrollOfUpgrade.upgrade( Dungeon.hero ); + Item.evoke( Dungeon.hero ); + + if (first.isEquipped( Dungeon.hero )) { + ((EquipableItem)first).doUnequip( Dungeon.hero, true ); + } + first.upgrade(); + GLog.p( TXT_LOOKS_BETTER, first.name() ); + Dungeon.hero.spendAndNext( 2f ); + Badges.validateItemLevelAquired( first ); + + if (second.isEquipped( Dungeon.hero )) { + ((EquipableItem)second).doUnequip( Dungeon.hero, false ); + } + second.detachAll( Dungeon.hero.belongings.backpack ); + + Quest.reforged = true; + + Journal.remove( Journal.Feature.TROLL ); + } + + @Override + public int defenseSkill( Char enemy ) { + return 1000; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public void add( Buff buff ) { + } + + @Override + public boolean reset() { + return true; + } + + @Override + public String description() { + return + "This troll blacksmith looks like all trolls look: he is tall and lean, and his skin resembles stone " + + "in both color and texture. The troll blacksmith is tinkering with unproportionally small tools."; + } + + public static class Quest { + + private static boolean spawned; + + private static boolean alternative; + private static boolean given; + private static boolean completed; + private static boolean reforged; + + public static void reset() { + spawned = false; + given = false; + completed = false; + reforged = false; + } + + private static final String NODE = "blacksmith"; + + private static final String SPAWNED = "spawned"; + private static final String ALTERNATIVE = "alternative"; + private static final String GIVEN = "given"; + private static final String COMPLETED = "completed"; + private static final String REFORGED = "reforged"; + + public static void storeInBundle( Bundle bundle ) { + + Bundle node = new Bundle(); + + node.put( SPAWNED, spawned ); + + if (spawned) { + node.put( ALTERNATIVE, alternative ); + node.put( GIVEN, given ); + node.put( COMPLETED, completed ); + node.put( REFORGED, reforged ); + } + + bundle.put( NODE, node ); + } + + public static void restoreFromBundle( Bundle bundle ) { + + Bundle node = bundle.getBundle( NODE ); + + if (!node.isNull() && (spawned = node.getBoolean( SPAWNED ))) { + alternative = node.getBoolean( ALTERNATIVE ); + given = node.getBoolean( GIVEN ); + completed = node.getBoolean( COMPLETED ); + reforged = node.getBoolean( REFORGED ); + } else { + reset(); + } + } + + public static void spawn( Collection rooms ) { + if (!spawned && Dungeon.depth > 11 && Random.Int( 15 - Dungeon.depth ) == 0) { + + Room blacksmith = null; + for (Room r : rooms) { + if (r.type == Type.STANDARD && r.width() > 4 && r.height() > 4) { + blacksmith = r; + blacksmith.type = Type.BLACKSMITH; + + spawned = true; + alternative = Random.Int( 2 ) == 0; + + given = false; + + break; + } + } + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/Ghost.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Ghost.java new file mode 100644 index 0000000000..7af8f3a099 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Ghost.java @@ -0,0 +1,395 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ParalyticGas; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.quest.DriedRose; +import com.watabou.pixeldungeon.items.quest.RatSkull; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.missiles.MissileWeapon; +import com.watabou.pixeldungeon.levels.SewerLevel; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.FetidRatSprite; +import com.watabou.pixeldungeon.sprites.GhostSprite; +import com.watabou.pixeldungeon.windows.WndQuest; +import com.watabou.pixeldungeon.windows.WndSadGhost; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Ghost extends Mob.NPC { + + { + name = "sad ghost"; + spriteClass = GhostSprite.class; + + flying = true; + + state = State.WANDERING; + } + + private static final String TXT_ROSE1 = + "Hello adventurer... Once I was like you - strong and confident... " + + "And now I'm dead... But I can't leave this place... Not until I have my _dried rose_... " + + "It's very important to me... Some monster stole it from my body..."; + + private static final String TXT_ROSE2 = + "Please... Help me... Find the rose..."; + + private static final String TXT_RAT1 = + "Hello adventurer... Once I was like you - strong and confident... " + + "And now I'm dead... But I can't leave this place... Not until I have my revenge... " + + "Slay the _fetid rat_, that has taken my life..."; + + private static final String TXT_RAT2 = + "Please... Help me... Slay the abomination..."; + + + public Ghost() { + super(); + + Sample.INSTANCE.load( Assets.SND_GHOST ); + } + + @Override + public int defenseSkill( Char enemy ) { + return 1000; + } + + @Override + public String defenseVerb() { + return "evaded"; + } + + @Override + public float speed() { + return 0.5f; + } + + @Override + protected Char chooseEnemy() { + return DUMMY; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public void add( Buff buff ) { + } + + @Override + public boolean reset() { + return true; + } + + @Override + public void interact() { + sprite.turnTo( pos, Dungeon.hero.pos ); + + Sample.INSTANCE.play( Assets.SND_GHOST ); + + if (Quest.given) { + + Item item = Quest.alternative ? + Dungeon.hero.belongings.getItem( RatSkull.class ) : + Dungeon.hero.belongings.getItem( DriedRose.class ); + if (item != null) { + GameScene.show( new WndSadGhost( this, item ) ); + } else { + GameScene.show( new WndQuest( this, Quest.alternative ? TXT_RAT2 : TXT_ROSE2 ) ); + + int newPos = -1; + for (int i=0; i < 10; i++) { + newPos = Dungeon.level.randomRespawnCell(); + if (newPos != -1) { + break; + } + } + if (newPos != -1) { + + Actor.freeCell( pos ); + + CellEmitter.get( pos ).start( Speck.factory( Speck.LIGHT ), 0.2f, 3 ); + pos = newPos; + sprite.place( pos ); + sprite.visible = Dungeon.visible[pos]; + } + } + + } else { + GameScene.show( new WndQuest( this, Quest.alternative ? TXT_RAT1 : TXT_ROSE1 ) ); + Quest.given = true; + + Journal.add( Journal.Feature.GHOST ); + } + } + + @Override + public String description() { + return + "The ghost is barely visible. It looks like a shapeless " + + "spot of faint light with a sorrowful face."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Paralysis.class ); + IMMUNITIES.add( Roots.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + + public static class Quest { + + private static boolean spawned; + + private static boolean alternative; + + private static boolean given; + + private static boolean processed; + + private static int depth; + + private static int left2kill; + + public static Weapon weapon; + public static Armor armor; + + public static void reset() { + spawned = false; + + weapon = null; + armor = null; + } + + private static final String NODE = "sadGhost"; + + private static final String SPAWNED = "spawned"; + private static final String ALTERNATIVE = "alternative"; + private static final String LEFT2KILL = "left2kill"; + private static final String GIVEN = "given"; + private static final String PROCESSED = "processed"; + private static final String DEPTH = "depth"; + private static final String WEAPON = "weapon"; + private static final String ARMOR = "armor"; + + public static void storeInBundle( Bundle bundle ) { + + Bundle node = new Bundle(); + + node.put( SPAWNED, spawned ); + + if (spawned) { + + node.put( ALTERNATIVE, alternative ); + if (!alternative) { + node.put( LEFT2KILL, left2kill ); + } + + node.put( GIVEN, given ); + node.put( DEPTH, depth ); + node.put( PROCESSED, processed ); + + node.put( WEAPON, weapon ); + node.put( ARMOR, armor ); + } + + bundle.put( NODE, node ); + } + + public static void restoreFromBundle( Bundle bundle ) { + + Bundle node = bundle.getBundle( NODE ); + + if (!node.isNull() && (spawned = node.getBoolean( SPAWNED ))) { + + alternative = node.getBoolean( ALTERNATIVE ); + if (!alternative) { + left2kill = node.getInt( LEFT2KILL ); + } + + given = node.getBoolean( GIVEN ); + depth = node.getInt( DEPTH ); + processed = node.getBoolean( PROCESSED ); + + weapon = (Weapon)node.get( WEAPON ); + armor = (Armor)node.get( ARMOR ); + } else { + reset(); + } + } + + public static void spawn( SewerLevel level ) { + if (!spawned && Dungeon.depth > 1 && Random.Int( 5 - Dungeon.depth ) == 0) { + + Ghost ghost = new Ghost(); + do { + ghost.pos = level.randomRespawnCell(); + } while (ghost.pos == -1); + level.mobs.add( ghost ); + Actor.occupyCell( ghost ); + + spawned = true; + alternative = Random.Int( 2 ) == 0; + if (!alternative) { + left2kill = 8; + } + + given = false; + processed = false; + depth = Dungeon.depth; + + do { + weapon = (Weapon)Generator.random( Generator.Category.WEAPON ); + } while (weapon instanceof MissileWeapon); + armor = (Armor)Generator.random( Generator.Category.ARMOR ); + + for (int i=0; i < 3; i++) { + Item another; + do { + another = Generator.random( Generator.Category.WEAPON ); + } while (another instanceof MissileWeapon); + if (another.level > weapon.level) { + weapon = (Weapon)another; + } + another = Generator.random( Generator.Category.ARMOR ); + if (another.level > armor.level) { + armor = (Armor)another; + } + } + weapon.identify(); + armor.identify(); + } + } + + public static void process( int pos ) { + if (spawned && given && !processed && (depth == Dungeon.depth)) { + if (alternative) { + + FetidRat rat = new FetidRat(); + rat.state = Mob.State.WANDERING; + rat.pos = Dungeon.level.randomRespawnCell(); + if (rat.pos != -1) { + GameScene.add( rat ); + processed = true; + } + + } else { + + if (Random.Int( left2kill ) == 0) { + Dungeon.level.drop( new DriedRose(), pos ).sprite.drop(); + processed = true; + } else { + left2kill--; + } + + } + } + } + + public static void complete() { + weapon = null; + armor = null; + + Journal.remove( Journal.Feature.GHOST ); + } + } + + public static class FetidRat extends Mob { + + { + name = "fetid rat"; + spriteClass = FetidRatSprite.class; + + HP = HT = 15; + defenseSkill = 5; + + EXP = 0; + maxLvl = 5; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 2, 6 ); + } + + @Override + public int attackSkill( Char target ) { + return 12; + } + + @Override + public int dr() { + return 2; + } + + @Override + public int defenseProc( Char enemy, int damage ) { + + GameScene.add( Blob.seed( pos, 20, ParalyticGas.class ) ); + + return super.defenseProc(enemy, damage); + } + + @Override + public void die( Object cause ) { + super.die( cause ); + + Dungeon.level.drop( new RatSkull(), pos ).sprite.drop(); + } + + @Override + public String description() { + return + "This marsupial rat is much larger, than a regular one. It is surrounded by a foul cloud."; + } + + private static final HashSet> IMMUNITIES = new HashSet>(); + static { + IMMUNITIES.add( Paralysis.class ); + } + + @Override + public HashSet> immunities() { + return IMMUNITIES; + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/Imp.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Imp.java new file mode 100644 index 0000000000..c256246b9c --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Imp.java @@ -0,0 +1,256 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.mobs.Golem; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.actors.mobs.Monk; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.quest.DwarfToken; +import com.watabou.pixeldungeon.items.rings.Ring; +import com.watabou.pixeldungeon.levels.CityLevel; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ImpSprite; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndImp; +import com.watabou.pixeldungeon.windows.WndQuest; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Imp extends Mob.NPC { + + { + name = "ambitious imp"; + spriteClass = ImpSprite.class; + } + + private static final String TXT_GOLEMS1 = + "Are you an adventurer? I love adventurers! You can always rely on them " + + "if something needs to be killed. Am I right? For bounty of course ;)\n" + + "In my case this is _golems_ who need to be killed. You see, I'm going to start a " + + "little business here, but these stupid golems are bad for business! " + + "It's very hard to negotiate with wandering lumps of granite, damn them! " + + "So please, kill... let's say _6 of them_ and a reward is yours."; + + private static final String TXT_MONKS1 = + "Are you an adventurer? I love adventurers! You can always rely on them " + + "if something needs to be killed. Am I right? For bounty of course ;)\n" + + "In my case this is _monks_ who need to be killed. You see, I'm going to start a " + + "little business here, but these lunatics don't buy anything themselves and " + + "will scare away other customers. " + + "So please, kill... let's say _8 of them_ and a reward is yours."; + + private static final String TXT_GOLEMS2 = + "How is your golem safari going?"; + + private static final String TXT_MONKS2 = + "Oh, you are still alive! I knew that your kung-fu is stronger ;) " + + "Just don't forget to grab these monks' tokens."; + + private static final String TXT_CYA = "See you, %s!"; + private static final String TXT_HEY = "Psst, %s!"; + + private boolean seenBefore = false; + + @Override + protected boolean act() { + + if (!Quest.given && Dungeon.visible[pos]) { + if (!seenBefore) { + yell( Utils.format( TXT_HEY, Dungeon.hero.className() ) ); + } + seenBefore = true; + } else { + seenBefore = false; + } + + throwItem(); + + return super.act(); + } + + @Override + public int defenseSkill( Char enemy ) { + return 1000; + } + + @Override + public String defenseVerb() { + return "evaded"; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public void add( Buff buff ) { + } + + @Override + public boolean reset() { + return true; + } + + @Override + public void interact() { + + sprite.turnTo( pos, Dungeon.hero.pos ); + if (Quest.given) { + + DwarfToken tokens = Dungeon.hero.belongings.getItem( DwarfToken.class ); + if (tokens != null && (tokens.quantity() >= 8 || (!Quest.alternative && tokens.quantity() >= 6))) { + GameScene.show( new WndImp( this, tokens ) ); + } else { + tell( Quest.alternative ? TXT_MONKS2 : TXT_GOLEMS2, Dungeon.hero.className() ); + } + + } else { + tell( Quest.alternative ? TXT_MONKS1 : TXT_GOLEMS1 ); + Quest.given = true; + Quest.completed = false; + + Journal.add( Journal.Feature.IMP ); + } + } + + private void tell( String format, Object...args ) { + GameScene.show( + new WndQuest( this, Utils.format( format, args ) ) ); + } + + public void flee() { + + yell( Utils.format( TXT_CYA, Dungeon.hero.className() ) ); + + destroy(); + sprite.die(); + } + + @Override + public String description() { + return + "Imps are lesser demons. They are notable for neither their strength nor their magic talent, " + + "but they are quite smart and sociable. Many imps prefer to live among non-demons."; + } + + public static class Quest { + + private static boolean alternative; + + private static boolean spawned; + private static boolean given; + private static boolean completed; + + public static Ring reward; + + public static void reset() { + spawned = false; + + reward = null; + } + + private static final String NODE = "demon"; + + private static final String ALTERNATIVE = "alternative"; + private static final String SPAWNED = "spawned"; + private static final String GIVEN = "given"; + private static final String COMPLETED = "completed"; + private static final String REWARD = "reward"; + + public static void storeInBundle( Bundle bundle ) { + + Bundle node = new Bundle(); + + node.put( SPAWNED, spawned ); + + if (spawned) { + node.put( ALTERNATIVE, alternative ); + + node.put( GIVEN, given ); + node.put( COMPLETED, completed ); + node.put( REWARD, reward ); + } + + bundle.put( NODE, node ); + } + + public static void restoreFromBundle( Bundle bundle ) { + + Bundle node = bundle.getBundle( NODE ); + + if (!node.isNull() && (spawned = node.getBoolean( SPAWNED ))) { + alternative = node.getBoolean( ALTERNATIVE ); + + given = node.getBoolean( GIVEN ); + completed = node.getBoolean( COMPLETED ); + reward = (Ring)node.get( REWARD ); + } + } + + public static void spawn( CityLevel level, Room room ) { + if (!spawned && Dungeon.depth > 16 && Random.Int( 20 - Dungeon.depth ) == 0) { + + Imp npc = new Imp(); + do { + npc.pos = level.randomRespawnCell(); + } while (npc.pos == -1 || level.heaps.get( npc.pos ) != null); + level.mobs.add( npc ); + Actor.occupyCell( npc ); + + spawned = true; + alternative = Random.Int( 2 ) == 0; + + given = false; + + do { + reward = (Ring)Generator.random( Generator.Category.RING ); + } while (reward.cursed); + reward.upgrade( 2 ); + reward.cursed = true; + } + } + + public static void process( Mob mob ) { + if (spawned && given && !completed) { + if ((alternative && mob instanceof Monk) || + (!alternative && mob instanceof Golem)) { + + Dungeon.level.drop( new DwarfToken(), mob.pos ).sprite.drop(); + } + } + } + + public static void complete() { + reward = null; + completed = true; + + Journal.remove( Journal.Feature.IMP ); + } + + public static boolean isCompleted() { + return completed; + } + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/ImpShopkeeper.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/ImpShopkeeper.java new file mode 100644 index 0000000000..eed5255bec --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/ImpShopkeeper.java @@ -0,0 +1,71 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.sprites.ImpSprite; +import com.watabou.pixeldungeon.utils.Utils; + +public class ImpShopkeeper extends Shopkeeper { + + private static final String TXT_GREETINGS = "Hello, friend!"; + + { + name = "ambitious imp"; + spriteClass = ImpSprite.class; + } + + private boolean seenBefore = false; + + @Override + protected boolean act() { + + if (!seenBefore && Dungeon.visible[pos]) { + yell( Utils.format( TXT_GREETINGS ) ); + seenBefore = true; + } + + return super.act(); + } + + @Override + protected void flee() { + for (Heap heap: Dungeon.level.heaps.values()) { + if (heap.type == Heap.Type.FOR_SALE) { + CellEmitter.get( heap.pos ).burst( ElmoParticle.FACTORY, 4 ); + heap.destroy(); + } + } + + destroy(); + + sprite.emitter().burst( Speck.factory( Speck.WOOL ), 15 ); + sprite.killAndErase(); + } + + @Override + public String description() { + return + "Imps are lesser demons. They are notable for neither their strength nor their magic talent. " + + "But they are quite smart and sociable, and many of imps prefer to live and do business among non-demons."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/MirrorImage.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/MirrorImage.java new file mode 100644 index 0000000000..813e036a5b --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/MirrorImage.java @@ -0,0 +1,138 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.MirrorSprite; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class MirrorImage extends Mob.NPC { + + { + name = "mirror image"; + spriteClass = MirrorSprite.class; + + state = State.HUNTING; + + enemy = DUMMY; + } + + public int tier; + + private int attack; + private int damage; + + private static final String TIER = "tier"; + private static final String ATTACK = "attack"; + private static final String DAMAGE = "damage"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( TIER, tier ); + bundle.put( ATTACK, attack ); + bundle.put( DAMAGE, damage ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + tier = bundle.getInt( TIER ); + attack = bundle.getInt( ATTACK ); + damage = bundle.getInt( DAMAGE ); + } + + public void duplicate( Hero hero ) { + tier = hero.tier(); + attack = hero.attackSkill( hero ); + damage = hero.damageRoll(); + } + + @Override + public int attackSkill( Char target ) { + return attack; + } + + @Override + public int damageRoll() { + return damage; + } + + @Override + public int attackProc( Char enemy, int damage ) { + int dmg = super.attackProc( enemy, damage ); + + destroy(); + sprite.die(); + + return dmg; + } + + protected Char chooseEnemy() { + + if (enemy == DUMMY || !enemy.isAlive()) { + HashSet enemies = new HashSet(); + for (Mob mob:Dungeon.level.mobs) { + if (mob.hostile && Level.fieldOfView[mob.pos]) { + enemies.add( mob ); + } + } + + enemy = enemies.size() > 0 ? Random.element( enemies ) : DUMMY; + } + + return enemy; + } + + @Override + public String description() { + return + "This illusion bears a close resemblance to you, " + + "but it's paler and twitches a little."; + } + + @Override + public CharSprite sprite() { + CharSprite s = super.sprite(); + ((MirrorSprite)s).updateArmor( tier ); + return s; + } + + @Override + public void interact() { + + int curPos = pos; + + moveSprite( pos, Dungeon.hero.pos ); + move( Dungeon.hero.pos ); + + Dungeon.hero.sprite.move( Dungeon.hero.pos, curPos ); + Dungeon.hero.move( curPos ); + + Dungeon.hero.spend( 1 / Dungeon.hero.speed() ); + Dungeon.hero.busy(); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/RatKing.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/RatKing.java new file mode 100644 index 0000000000..67c4901210 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/RatKing.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.mobs.Mob; + +import com.watabou.pixeldungeon.sprites.RatKingSprite; + +public class RatKing extends Mob.NPC { + + { + name = "rat king"; + spriteClass = RatKingSprite.class; + + state = State.SLEEPING; + } + + @Override + public int defenseSkill( Char enemy ) { + return 1000; + } + + @Override + public float speed() { + return 2f; + } + + @Override + protected Char chooseEnemy() { + return DUMMY; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public void add( Buff buff ) { + } + + @Override + public boolean reset() { + return true; + } + + @Override + public void interact() { + sprite.turnTo( pos, Dungeon.hero.pos ); + if (state == State.SLEEPING) { + notice(); + yell( "I'm not sleeping!" ); + state = State.WANDERING; + } else { + yell( "What is it? I have no time for this nonsense. My kingdom won't rule itself!" ); + } + } + + @Override + public String description() { + return + "This rat is a little bigger than a regular marsupial rat " + + "and it's wearing a tiny crown on its head."; + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/Shopkeeper.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Shopkeeper.java new file mode 100644 index 0000000000..363b9b6689 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Shopkeeper.java @@ -0,0 +1,103 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ShopkeeperSprite; +import com.watabou.pixeldungeon.windows.WndBag; +import com.watabou.pixeldungeon.windows.WndTradeItem; + +public class Shopkeeper extends Mob.NPC { + + { + name = "shopkeeper"; + spriteClass = ShopkeeperSprite.class; + } + + @Override + protected boolean act() { + + throwItem(); + + sprite.turnTo( pos, Dungeon.hero.pos ); + spend( TICK ); + return true; + } + + @Override + public void damage( int dmg, Object src ) { + flee(); + } + + @Override + public void add( Buff buff ) { + flee(); + } + + protected void flee() { + for (Heap heap: Dungeon.level.heaps.values()) { + if (heap.type == Heap.Type.FOR_SALE) { + CellEmitter.get( heap.pos ).burst( ElmoParticle.FACTORY, 4 ); + heap.destroy(); + } + } + + destroy(); + + sprite.killAndErase(); + CellEmitter.get( pos ).burst( ElmoParticle.FACTORY, 6 ); + } + + @Override + public boolean reset() { + return true; + } + + @Override + public String description() { + return + "This stout guy looks more appropriate for a trade district in some large city " + + "than for a dungeon. His prices explain why he prefers to do business here."; + } + + public static WndBag sell() { + return GameScene.selectItem( itemSelector, WndBag.Mode.FOR_SALE, "Select an item to sell" ); + } + + private static WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + WndBag parentWnd = sell(); + GameScene.show( new WndTradeItem( item, parentWnd ) ); + } + } + }; + + @Override + public void interact() { + sell(); + } +} diff --git a/src/com/watabou/pixeldungeon/actors/mobs/npcs/Wandmaker.java b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Wandmaker.java new file mode 100644 index 0000000000..9a68d05557 --- /dev/null +++ b/src/com/watabou/pixeldungeon/actors/mobs/npcs/Wandmaker.java @@ -0,0 +1,379 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.actors.mobs.npcs; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.items.potions.PotionOfStrength; +import com.watabou.pixeldungeon.items.quest.CorpseDust; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.items.wands.WandOfAmok; +import com.watabou.pixeldungeon.items.wands.WandOfAvalanche; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.items.wands.WandOfDisintegration; +import com.watabou.pixeldungeon.items.wands.WandOfFirebolt; +import com.watabou.pixeldungeon.items.wands.WandOfLightning; +import com.watabou.pixeldungeon.items.wands.WandOfPoison; +import com.watabou.pixeldungeon.items.wands.WandOfRegrowth; +import com.watabou.pixeldungeon.items.wands.WandOfSlowness; +import com.watabou.pixeldungeon.items.wands.WandOfTelekinesis; +import com.watabou.pixeldungeon.levels.PrisonLevel; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.plants.Plant; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.sprites.WandmakerSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndQuest; +import com.watabou.pixeldungeon.windows.WndWandmaker; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Wandmaker extends Mob.NPC { + + { + name = "old wandmaker"; + spriteClass = WandmakerSprite.class; + } + + private static final String TXT_BERRY1 = + "Oh, what a pleasant surprise to meet a decent person in such place! I came here for a rare ingredient - " + + "a _Rotberry seed_. Being a magic user, I'm quite able to defend myself against local monsters, " + + "but I'm getting lost in no time, it's very embarrassing. Probably you could help me? I would be " + + "happy to pay for your service with one of my best wands."; + + private static final String TXT_DUST1 = + "Oh, what a pleasant surprise to meet a decent person in such place! I came here for a rare ingredient - " + + "_corpse dust_. It can be gathered from skeletal remains and there is an ample number of them in the dungeon. " + + "Being a magic user, I'm quite able to defend myself against local monsters, but I'm getting lost in no time, " + + "it's very embarrassing. Probably you could help me? I would be happy to pay for your service with one of my best wands."; + + private static final String TXT_BERRY2 = + "Any luck with a Rotberry seed, %s? No? Don't worry, I'm not in a hurry."; + + private static final String TXT_DUST2 = + "Any luck with corpse dust, %s? Bone piles are the most obvious places to look."; + + @Override + protected boolean act() { + throwItem(); + return super.act(); + } + + @Override + public int defenseSkill( Char enemy ) { + return 1000; + } + + @Override + public String defenseVerb() { + return "absorbed"; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public void add( Buff buff ) { + } + + @Override + public boolean reset() { + return true; + } + + @Override + public void interact() { + + sprite.turnTo( pos, Dungeon.hero.pos ); + if (Quest.given) { + + Item item = Quest.alternative ? + Dungeon.hero.belongings.getItem( CorpseDust.class ) : + Dungeon.hero.belongings.getItem( Rotberry.Seed.class ); + if (item != null) { + GameScene.show( new WndWandmaker( this, item ) ); + } else { + tell( Quest.alternative ? TXT_DUST2 : TXT_BERRY2, Dungeon.hero.className() ); + } + + } else { + tell( Quest.alternative ? TXT_DUST1 : TXT_BERRY1 ); + Quest.given = true; + + Quest.placeItem(); + + Journal.add( Journal.Feature.WANDMAKER ); + } + } + + private void tell( String format, Object...args ) { + GameScene.show( new WndQuest( this, Utils.format( format, args ) ) ); + } + + @Override + public String description() { + return + "This old but hale gentleman wears a slightly confused " + + "expression. He is protected by a magic shield."; + } + + public static class Quest { + + private static boolean spawned; + + private static boolean alternative; + + private static boolean given; + + public static Wand wand1; + public static Wand wand2; + + public static void reset() { + spawned = false; + + wand1 = null; + wand2 = null; + } + + private static final String NODE = "wandmaker"; + + private static final String SPAWNED = "spawned"; + private static final String ALTERNATIVE = "alternative"; + private static final String GIVEN = "given"; + private static final String WAND1 = "wand1"; + private static final String WAND2 = "wand2"; + + public static void storeInBundle( Bundle bundle ) { + + Bundle node = new Bundle(); + + node.put( SPAWNED, spawned ); + + if (spawned) { + + node.put( ALTERNATIVE, alternative ); + + node.put(GIVEN, given ); + + node.put( WAND1, wand1 ); + node.put( WAND2, wand2 ); + } + + bundle.put( NODE, node ); + } + + public static void restoreFromBundle( Bundle bundle ) { + + Bundle node = bundle.getBundle( NODE ); + + if (!node.isNull() && (spawned = node.getBoolean( SPAWNED ))) { + + alternative = node.getBoolean( ALTERNATIVE ); + + given = node.getBoolean( GIVEN ); + + wand1 = (Wand)node.get( WAND1 ); + wand2 = (Wand)node.get( WAND2 ); + } else { + reset(); + } + } + + public static void spawn( PrisonLevel level, Room room ) { + if (!spawned && Dungeon.depth > 6 && Random.Int( 10 - Dungeon.depth ) == 0) { + + Wandmaker npc = new Wandmaker(); + do { + npc.pos = room.random(); + } while (level.map[npc.pos] == Terrain.ENTRANCE || level.map[npc.pos] == Terrain.SIGN); + level.mobs.add( npc ); + Actor.occupyCell( npc ); + + spawned = true; + alternative = Random.Int( 2 ) == 0; + + given = false; + + switch (Random.Int( 5 )) { + case 0: + wand1 = new WandOfAvalanche(); + break; + case 1: + wand1 = new WandOfDisintegration(); + break; + case 2: + wand1 = new WandOfFirebolt(); + break; + case 3: + wand1 = new WandOfLightning(); + break; + case 4: + wand1 = new WandOfPoison(); + break; + } + wand1.random().upgrade(); + + switch (Random.Int( 5 )) { + case 0: + wand2 = new WandOfAmok(); + break; + case 1: + wand2 = new WandOfBlink(); + break; + case 2: + wand2 = new WandOfRegrowth(); + break; + case 3: + wand2 = new WandOfSlowness(); + break; + case 4: + wand2 = new WandOfTelekinesis(); + break; + } + wand2.random().upgrade(); + } + } + + public static void placeItem() { + if (alternative) { + + ArrayList candidates = new ArrayList(); + for (Heap heap : Dungeon.level.heaps.values()) { + if (heap.type == Heap.Type.SKELETON && !Dungeon.visible[heap.pos]) { + candidates.add( heap ); + } + } + + if (candidates.size() > 0) { + Random.element( candidates ).drop( new CorpseDust() ); + } else { + int pos = Dungeon.level.randomRespawnCell(); + while (Dungeon.level.heaps.get( pos ) != null) { + pos = Dungeon.level.randomRespawnCell(); + } + + Heap heap = Dungeon.level.drop( new CorpseDust(), pos ); + heap.type = Heap.Type.SKELETON; + heap.sprite.link(); + } + + } else { + + int shrubPos = Dungeon.level.randomRespawnCell(); + while (Dungeon.level.heaps.get( shrubPos ) != null) { + shrubPos = Dungeon.level.randomRespawnCell(); + } + Dungeon.level.plant( new Rotberry.Seed(), shrubPos ); + + } + } + + public static void complete() { + wand1 = null; + wand2 = null; + + Journal.remove( Journal.Feature.WANDMAKER ); + } + } + + public static class Rotberry extends Plant { + + private static final String TXT_DESC = + "Berries of this shrub taste like sweet, sweet death."; + + { + image = 7; + plantName = "Rotberry"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + GameScene.add( Blob.seed( pos, 100, ToxicGas.class ) ); + + Dungeon.level.drop( new Seed(), pos ).sprite.drop(); + + if (ch != null) { + Buff.prolong( ch, Roots.class, TICK * 3 ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Rotberry"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_ROTBERRY; + + plantClass = Rotberry.class; + alchemyClass = PotionOfStrength.class; + } + + @Override + public boolean collect( Bag container ) { + if (super.collect( container )) { + + if (Dungeon.level != null) { + for (Mob mob : Dungeon.level.mobs) { + mob.beckon( Dungeon.hero.pos ); + } + + GLog.w( "The seed emits a roar that echoes throughout the dungeon!" ); + CellEmitter.center( Dungeon.hero.pos ).start( Speck.factory( Speck.SCREAM ), 0.3f, 3 ); + Sample.INSTANCE.play( Assets.SND_CHALLENGE ); + } + + return true; + } else { + return false; + } + } + + @Override + public String desc() { + return TXT_DESC; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/BadgeBanner.java b/src/com/watabou/pixeldungeon/effects/BadgeBanner.java new file mode 100644 index 0000000000..2fc334e5dd --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/BadgeBanner.java @@ -0,0 +1,284 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.PointF; + +public class BadgeBanner extends Image { + + private enum State { + FADE_IN, STATIC, FADE_OUT + }; + private State state; + + private static final float DEFAULT_SCALE = 3; + + private static final float FADE_IN_TIME = 0.2f; + private static final float STATIC_TIME = 1f; + private static final float FADE_OUT_TIME = 1.0f; + + private int index; + private float time; + + private static TextureFilm atlas; + + private static BadgeBanner current; + + private BadgeBanner( int index ) { + + super( Assets.BADGES ); + + if (atlas == null) { + atlas = new TextureFilm( texture, 16, 16 ); + } + + this.index = index; + + frame( atlas.get( index ) ); + origin.set( width / 2, height / 2 ); + + alpha( 0 ); + scale.set( 2 * DEFAULT_SCALE ); + + state = State.FADE_IN; + time = FADE_IN_TIME; + + Sample.INSTANCE.play( Assets.SND_BADGE ); + } + + @Override + public void update() { + super.update(); + + time -= Game.elapsed; + if (time >= 0) { + + switch (state) { + case FADE_IN: + float p = time / FADE_IN_TIME; + scale.set( (1 + p) * DEFAULT_SCALE ); + alpha( 1 - p ); + break; + case STATIC: + break; + case FADE_OUT: + alpha( time / FADE_OUT_TIME ); + break; + } + + } else { + + switch (state) { + case FADE_IN: + time = STATIC_TIME; + state = State.STATIC; + scale.set( DEFAULT_SCALE ); + alpha( 1 ); + highlight( this, index ); + break; + case STATIC: + time = FADE_OUT_TIME; + state = State.FADE_OUT; + break; + case FADE_OUT: + killAndErase(); + break; + } + + } + } + + @Override + public void kill() { + if (current == this) { + current = null; + } + super.kill(); + } + + public static void highlight( Image image, int index ) { + + PointF p = new PointF(); + + switch (index) { + case 0: + case 1: + case 2: + case 3: + p.offset( 7, 3 ); + break; + case 4: + case 5: + case 6: + case 7: + p.offset( 6, 5 ); + break; + case 8: + case 9: + case 10: + case 11: + p.offset( 6, 3 ); + break; + case 12: + case 13: + case 14: + case 15: + p.offset( 7, 4 ); + break; + case 16: + p.offset( 6, 3 ); + break; + case 17: + p.offset( 5, 4 ); + break; + case 18: + p.offset( 7, 3 ); + break; + case 20: + p.offset( 7, 3 ); + break; + case 21: + p.offset( 7, 3 ); + break; + case 22: + p.offset( 6, 4 ); + break; + case 23: + p.offset( 4, 5 ); + break; + case 24: + p.offset( 6, 4 ); + break; + case 25: + p.offset( 6, 5 ); + break; + case 26: + p.offset( 5, 5 ); + break; + case 27: + p.offset( 6, 4 ); + break; + case 28: + p.offset( 3, 5 ); + break; + case 29: + p.offset( 5, 4 ); + break; + case 30: + p.offset( 5, 4 ); + break; + case 31: + p.offset( 5, 5 ); + break; + case 32: + case 33: + p.offset( 7, 4 ); + break; + case 34: + p.offset( 6, 4 ); + break; + case 35: + p.offset( 6, 4 ); + break; + case 36: + p.offset( 6, 5 ); + break; + case 37: + p.offset( 4, 4 ); + break; + case 38: + p.offset( 5, 5 ); + break; + case 40: + case 41: + case 42: + case 43: + p.offset( 5, 4 ); + break; + case 44: + case 45: + case 46: + case 47: + p.offset( 5, 5 ); + break; + case 48: + case 49: + case 50: + case 51: + p.offset( 7, 4 ); + break; + case 52: + case 53: + case 54: + case 55: + p.offset( 4, 4 ); + break; + case 56: + p.offset( 3, 7 ); + break; + case 57: + p.offset( 4, 5 ); + break; + case 58: + p.offset( 6, 4 ); + break; + case 59: + p.offset( 7, 4 ); + break; + case 60: + case 61: + case 62: + case 63: + p.offset( 4, 4 ); + break; + } + + p.x *= image.scale.x; + p.y *= image.scale.y; + p.offset( + -image.origin.x * (image.scale.x - 1), + -image.origin.y * (image.scale.y - 1) ); + p.offset( image.point() ); + + Speck star = new Speck(); + star.reset( 0, p.x, p.y, Speck.DISCOVER ); + star.camera = image.camera(); + image.parent.add( star ); + } + + public static BadgeBanner show( int image ) { + if (current != null) { + current.killAndErase(); + } + return (current = new BadgeBanner( image )); + } + + public static Image image( int index ) { + Image image = new Image( Assets.BADGES ); + if (atlas == null) { + atlas = new TextureFilm( image.texture, 16, 16 ); + } + image.frame( atlas.get( index ) ); + return image; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/BannerSprites.java b/src/com/watabou/pixeldungeon/effects/BannerSprites.java new file mode 100644 index 0000000000..3d323585ee --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/BannerSprites.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Assets; + +public class BannerSprites { + + public enum Type { + PIXEL_DUNGEON, + BOSS_SLAIN, + GAME_OVER + }; + + public static Image get( Type type ) { + Image icon = new Image( Assets.BANNERS ); + switch (type) { + case PIXEL_DUNGEON: + icon.frame( icon.texture.uvRect( 0, 0, 128, 70 ) ); + break; + case BOSS_SLAIN: + icon.frame( icon.texture.uvRect( 0, 70, 128, 105 ) ); + break; + case GAME_OVER: + icon.frame( icon.texture.uvRect( 0, 105, 128, 140 ) ); + break; + } + return icon; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/BlobEmitter.java b/src/com/watabou/pixeldungeon/effects/BlobEmitter.java new file mode 100644 index 0000000000..2e142195cc --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/BlobEmitter.java @@ -0,0 +1,59 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.utils.Random; + +public class BlobEmitter extends Emitter { + + private static final int WIDTH = Blob.WIDTH; + private static final int LENGTH = Blob.LENGTH; + + private Blob blob; + + public BlobEmitter( Blob blob ) { + + super(); + + this.blob = blob; + blob.use( this ); + } + + @Override + protected void emit( int index ) { + + if (blob.volume <= 0) { + return; + } + + int[] map = blob.cur; + float size = DungeonTilemap.SIZE; + + for (int i=0; i < LENGTH; i++) { + if (map[i] > 0 && Dungeon.visible[i]) { + float x = ((i % WIDTH) + Random.Float()) * size; + float y = ((i / WIDTH) + Random.Float()) * size; + factory.emit( this, index, x, y ); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/CellEmitter.java b/src/com/watabou/pixeldungeon/effects/CellEmitter.java new file mode 100644 index 0000000000..bb2751e290 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/CellEmitter.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.PointF; + +public class CellEmitter { + + public static Emitter get( int cell ) { + + PointF p = DungeonTilemap.tileToWorld( cell ); + + Emitter emitter = GameScene.emitter(); + emitter.pos( p.x, p.y, DungeonTilemap.SIZE, DungeonTilemap.SIZE ); + + return emitter; + } + + public static Emitter center( int cell ) { + + PointF p = DungeonTilemap.tileToWorld( cell ); + + Emitter emitter = GameScene.emitter(); + emitter.pos( p.x + DungeonTilemap.SIZE / 2, p.y + DungeonTilemap.SIZE / 2 ); + + return emitter; + } + + public static Emitter bottom( int cell ) { + + PointF p = DungeonTilemap.tileToWorld( cell ); + + Emitter emitter = GameScene.emitter(); + emitter.pos( p.x, p.y + DungeonTilemap.SIZE, DungeonTilemap.SIZE, 0 ); + + return emitter; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/CheckedCell.java b/src/com/watabou/pixeldungeon/effects/CheckedCell.java new file mode 100644 index 0000000000..154000b42f --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/CheckedCell.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.DungeonTilemap; + +public class CheckedCell extends Image { + + private float alpha; + + public CheckedCell( int pos ) { + super( TextureCache.createSolid( 0xFF55AAFF ) ); + + origin.set( 0.5f ); + + point( DungeonTilemap.tileToWorld( pos ).offset( + DungeonTilemap.SIZE / 2, + DungeonTilemap.SIZE / 2 ) ); + + alpha = 0.8f; + } + + @Override + public void update() { + if ((alpha -= Game.elapsed) > 0) { + alpha( alpha ); + scale.set( DungeonTilemap.SIZE * alpha ); + } else { + killAndErase(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/DeathRay.java b/src/com/watabou/pixeldungeon/effects/DeathRay.java new file mode 100644 index 0000000000..ba58b8f12f --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/DeathRay.java @@ -0,0 +1,75 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.PointF; + +public class DeathRay extends Image { + + private static final double A = 180 / Math.PI; + + private static final float DURATION = 0.5f; + + private float timeLeft; + + public DeathRay( PointF s, PointF e ) { + super( Effects.get( Effects.Type.RAY ) ); + + origin.set( 0, height / 2 ); + + x = s.x - origin.x; + y = s.y - origin.y; + + float dx = e.x - s.x; + float dy = e.y - s.y; + angle = (float)(Math.atan2( dy, dx ) * A); + scale.x = (float)Math.sqrt( dx * dx + dy * dy ) / width; + + Sample.INSTANCE.play( Assets.SND_RAY ); + + timeLeft = DURATION; + } + + @Override + public void update() { + super.update(); + + float p = timeLeft / DURATION; + alpha( p ); + scale.set( scale.x, p ); + + if ((timeLeft -= Game.elapsed) <= 0) { + killAndErase(); + } + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Effects.java b/src/com/watabou/pixeldungeon/effects/Effects.java new file mode 100644 index 0000000000..744961ce08 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Effects.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Assets; + +public class Effects { + + public enum Type { + RIPPLE, + LIGHTNING, + WOUND, + RAY + }; + + public static Image get( Type type ) { + Image icon = new Image( Assets.EFFECTS ); + switch (type) { + case RIPPLE: + icon.frame( icon.texture.uvRect( 0, 0, 16, 16 ) ); + break; + case LIGHTNING: + icon.frame( icon.texture.uvRect( 16, 0, 32, 8 ) ); + break; + case WOUND: + icon.frame( icon.texture.uvRect( 16, 8, 32, 16 ) ); + break; + case RAY: + icon.frame( icon.texture.uvRect( 16, 16, 32, 24 ) ); + break; + } + return icon; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/EmoIcon.java b/src/com/watabou/pixeldungeon/effects/EmoIcon.java new file mode 100644 index 0000000000..f7601c1976 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/EmoIcon.java @@ -0,0 +1,97 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.utils.Random; + +public class EmoIcon extends Image { + + protected float maxSize = 2; + protected float timeScale = 1; + + protected boolean growing = true; + + protected CharSprite owner; + + public EmoIcon( CharSprite owner ) { + super(); + + this.owner = owner; + GameScene.add( this ); + } + + @Override + public void update() { + super.update(); + + if (visible) { + if (growing) { + scale.set( scale.x + Game.elapsed * timeScale ); + if (scale.x > maxSize) { + growing = false; + } + } else { + scale.set( scale.x - Game.elapsed * timeScale ); + if (scale.x < 1) { + growing = true; + } + } + + x = owner.x + owner.width - width / 2; + y = owner.y - height; + } + } + + public static class Sleep extends EmoIcon { + + public Sleep( CharSprite owner ) { + + super( owner ); + + copy( Icons.get( Icons.SLEEP ) ); + + maxSize = 1.2f; + timeScale = 0.5f; + + origin.set( width / 2, height / 2 ); + scale.set( Random.Float( 1, maxSize ) ); + } + } + + public static class Alert extends EmoIcon { + + public Alert( CharSprite owner ) { + + super( owner ); + + copy( Icons.get( Icons.ALERT ) ); + + maxSize = 1.3f; + timeScale = 2; + + origin.set( 2.5f, height - 2.5f ); + scale.set( Random.Float( 1, maxSize ) ); + } + } + +} diff --git a/src/com/watabou/pixeldungeon/effects/Fireball.java b/src/com/watabou/pixeldungeon/effects/Fireball.java new file mode 100644 index 0000000000..70b366cbd6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Fireball.java @@ -0,0 +1,164 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.RectF; +import android.opengl.GLES20; + +import com.watabou.glwrap.Texture; +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.ColorMath; +import com.watabou.utils.Random; + +public class Fireball extends Component { + + private static final RectF BLIGHT = new RectF( 0, 0, 0.25f, 1 ); + private static final RectF FLIGHT = new RectF( 0.25f, 0, 0.5f, 1 ); + private static final RectF FLAME1 = new RectF( 0.50f, 0, 0.75f, 1 ); + private static final RectF FLAME2 = new RectF( 0.75f, 0, 1.00f, 1 ); + + private static final int COLOR = 0xFF66FF; + + private Image bLight; + private Image fLight; + private Emitter emitter; + private Group sparks; + + @Override + protected void createChildren() { + + sparks = new Group(); + add( sparks ); + + bLight = new Image( Assets.FIREBALL ); + bLight.frame( BLIGHT ); + bLight.origin.set( bLight.width / 2 ); + bLight.angularSpeed = -90; + add( bLight ); + + emitter = new Emitter(); + emitter.pour( new Emitter.Factory() { + @Override + public void emit(Emitter emitter, int index, float x, float y) { + Flame p = (Flame)emitter.recycle( Flame.class ); + p.reset(); + p.x = x - p.width / 2; + p.y = y - p.height / 2; + } + }, 0.1f ); + add( emitter ); + + fLight = new Image( Assets.FIREBALL ); + fLight.frame( FLIGHT ); + fLight.origin.set( fLight.width / 2 ); + fLight.angularSpeed = 360; + add( fLight ); + + bLight.texture.filter( Texture.LINEAR, Texture.LINEAR ); + } + + @Override + protected void layout() { + + bLight.x = x - bLight.width / 2; + bLight.y = y - bLight.height / 2; + + emitter.pos( + x - bLight.width / 4, + y - bLight.height / 4, + bLight.width / 2, + bLight.height / 2 ); + + fLight.x = x - fLight.width / 2; + fLight.y = y - fLight.height / 2; + } + + @Override + public void update() { + + super.update(); + + if (Random.Float() < Game.elapsed) { + PixelParticle spark = (PixelParticle)sparks.recycle( PixelParticle.Shrinking.class ); + spark.reset( x, y, ColorMath.random( COLOR, 0x66FF66 ), 2, Random.Float( 0.5f, 1.0f ) ); + spark.speed.set( + Random.Float( -40, +40 ), + Random.Float( -60, +20 ) ); + spark.acc.set( 0, +80 ); + sparks.add( spark ); + } + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } + + public static class Flame extends Image { + + private static float LIFESPAN = 1f; + + private static float SPEED = -40f; + private static float ACC = -20f; + + private float timeLeft; + + public Flame() { + + super( Assets.FIREBALL ); + + frame( Random.Int( 2 ) == 0 ? FLAME1 : FLAME2 ); + origin.set( width / 2, height / 2 ); + acc.set( 0, ACC ); + } + + public void reset() { + revive(); + timeLeft = LIFESPAN; + speed.set( 0, SPEED ); + } + + @Override + public void update() { + + super.update(); + + if ((timeLeft -= Game.elapsed) <= 0) { + + kill(); + + } else { + + float p = timeLeft / LIFESPAN; + scale.set( p ); + alpha( p > 0.8f ? (1 - p) * 5f : p * 1.25f ); + + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Flare.java b/src/com/watabou/pixeldungeon/effects/Flare.java new file mode 100644 index 0000000000..7aab1984b5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Flare.java @@ -0,0 +1,170 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + +import javax.microedition.khronos.opengles.GL10; + +import android.annotation.SuppressLint; +import android.opengl.GLES20; +import android.util.FloatMath; + +import com.watabou.gltextures.Gradient; +import com.watabou.gltextures.SmartTexture; +import com.watabou.noosa.Game; +import com.watabou.noosa.NoosaScript; +import com.watabou.noosa.Visual; + +public class Flare extends Visual { + + private float duration = 0; + private float lifespan; + + private boolean lightMode = true; + + private SmartTexture texture; + + private FloatBuffer vertices; + private ShortBuffer indices; + + private int nRays; + + @SuppressLint("FloatMath") + public Flare( int nRays, float radius ) { + + super( 0, 0, 0, 0 ); + + // Texture is incorrectly created every time we need + // to show the effect, it must be refactored + + int gradient[] = {0xFFFFFFFF, 0x00FFFFFF}; + texture = new Gradient( gradient ); + + this.nRays = nRays; + + angle = 45; + angularSpeed = 180; + + vertices = ByteBuffer. + allocateDirect( (nRays * 2 + 1) * 4 * (Float.SIZE / 8) ). + order( ByteOrder.nativeOrder() ). + asFloatBuffer(); + + indices = ByteBuffer. + allocateDirect( nRays * 3 * Short.SIZE / 8 ). + order( ByteOrder.nativeOrder() ). + asShortBuffer(); + + float v[] = new float[4]; + + v[0] = 0; + v[1] = 0; + v[2] = 0.25f; + v[3] = 0; + vertices.put( v ); + + v[2] = 0.75f; + v[3] = 0; + + for (int i=0; i < nRays; i++) { + + float a = i * 3.1415926f * 2 / nRays; + v[0] = FloatMath.cos( a ) * radius; + v[1] = FloatMath.sin( a ) * radius; + vertices.put( v ); + + a += 3.1415926f * 2 / nRays / 2; + v[0] = FloatMath.cos( a ) * radius; + v[1] = FloatMath.sin( a ) * radius; + vertices.put( v ); + + indices.put( (short)0 ); + indices.put( (short)(1 + i * 2) ); + indices.put( (short)(2 + i * 2) ); + } + + indices.position( 0 ); + } + + public Flare color( int color, boolean lightMode ) { + this.lightMode = lightMode; + hardlight( color ); + + return this; + } + + public Flare show( Visual visual, float duration ) { + point( visual.center() ); + visual.parent.addToBack( this ); + + lifespan = this.duration = duration; + + return this; + } + + @Override + public void update() { + super.update(); + + if (duration > 0) { + if ((lifespan -= Game.elapsed) > 0) { + + float p = 1 - lifespan / duration; // 0 -> 1 + p = p < 0.25f ? p * 4 : (1 - p) * 1.333f; + scale.set( p ); + alpha( p ); + + } else { + killAndErase(); + } + } + } + + @Override + public void draw() { + + super.draw(); + + if (lightMode) { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + drawRays(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } else { + drawRays(); + } + } + + private void drawRays() { + + NoosaScript script = NoosaScript.get(); + + texture.bind(); + + script.uModel.valueM4( matrix ); + script.lighting( + rm, gm, bm, am, + ra, ga, ba, aa ); + + script.camera( camera ); + script.drawElements( vertices, indices, nRays * 3 ); + } +} diff --git a/src/com/watabou/pixeldungeon/effects/FloatingText.java b/src/com/watabou/pixeldungeon/effects/FloatingText.java new file mode 100644 index 0000000000..86f05d4487 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/FloatingText.java @@ -0,0 +1,134 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import java.util.ArrayList; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.utils.SparseArray; + +public class FloatingText extends BitmapText { + + private static final float LIFESPAN = 1f; + private static final float DISTANCE = DungeonTilemap.SIZE; + + private float timeLeft; + + private int key = -1; + + private static SparseArray> stacks = new SparseArray>(); + + public FloatingText() { + + super(); + + PixelScene.chooseFont( 9 ); + font = PixelScene.font; + scale.set( PixelScene.scale ); + + speed.y = - DISTANCE / LIFESPAN; + } + + @Override + public void update() { + super.update(); + + if (timeLeft > 0) { + if ((timeLeft -= Game.elapsed) <= 0) { + kill(); + } else { + float p = timeLeft / LIFESPAN; + alpha( p > 0.5f ? 1 : p * 2 ); + } + } + } + + @Override + public void kill() { + if (key != -1) { + stacks.get( key ).remove( this ); + key = -1; + } + super.kill(); + } + + @Override + public void destroy() { + kill(); + super.destroy(); + } + + public void reset( float x, float y, String text, int color ) { + + revive(); + + text( text ); + hardlight( color ); + + measure(); + this.x = PixelScene.align( x - width() / 2 ); + this.y = y - height(); + + timeLeft = LIFESPAN; + } + + /* STATIC METHODS */ + + public static void show( float x, float y, String text, int color ) { + GameScene.status().reset( x, y, text, color ); + } + + public static void show( float x, float y, int key, String text, int color ) { + FloatingText txt = GameScene.status(); + txt.reset( x, y, text, color ); + push( txt, key ); + } + + private static void push( FloatingText txt, int key ) { + + txt.key = key; + + ArrayList stack = stacks.get( key ); + if (stack == null) { + stack = new ArrayList(); + stacks.put( key, stack ); + } + + if (stack.size() > 0) { + FloatingText below = txt; + int aboveIndex = stack.size() - 1; + while (aboveIndex >= 0) { + FloatingText above = stack.get( aboveIndex ); + if (above.y + above.height() > below.y) { + above.y = below.y - above.height(); + + below = above; + aboveIndex--; + } else { + break; + } + } + } + + stack.add( txt ); + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Halo.java b/src/com/watabou/pixeldungeon/effects/Halo.java new file mode 100644 index 0000000000..a9f42da1a7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Halo.java @@ -0,0 +1,74 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.watabou.gltextures.SmartTexture; +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.Image; + +public class Halo extends Image { + + private static final Object CACHE_KEY = Halo.class; + + protected static final int RADIUS = 64; + + protected float radius = RADIUS; + protected float brightness = 1; + + public Halo() { + super(); + + if (!TextureCache.contains( CACHE_KEY )) { + Bitmap bmp = Bitmap.createBitmap( RADIUS * 2, RADIUS * 2, Bitmap.Config.ARGB_8888 ); + Canvas canvas = new Canvas( bmp ); + Paint paint = new Paint(); + paint.setColor( 0xFFFFFFFF ); + canvas.drawCircle( RADIUS, RADIUS, RADIUS * 0.75f, paint ); + paint.setColor( 0x88FFFFFF ); + canvas.drawCircle( RADIUS, RADIUS, RADIUS, paint ); + TextureCache.add( CACHE_KEY, new SmartTexture( bmp ) ); + } + + texture( CACHE_KEY ); + + origin.set( RADIUS ); + } + + public Halo( float radius, int color, float brightness ) { + + this(); + + hardlight( color ); + alpha( this.brightness = brightness ); + radius( radius ); + } + + public Halo point( float x, float y ) { + this.x = x - RADIUS; + this.y = y - RADIUS; + return this; + } + + public void radius( float value ) { + scale.set( (this.radius = value) / RADIUS ); + } +} diff --git a/src/com/watabou/pixeldungeon/effects/IceBlock.java b/src/com/watabou/pixeldungeon/effects/IceBlock.java new file mode 100644 index 0000000000..24c69b568b --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/IceBlock.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Gizmo; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.sprites.CharSprite; + +public class IceBlock extends Gizmo { + + private float phase; + + private CharSprite target; + + public IceBlock( CharSprite target ) { + super(); + + this.target = target; + phase = 0; + } + + @Override + public void update() { + super.update(); + + if ((phase += Game.elapsed * 2) < 1) { + target.tint( 0.83f, 1.17f, 1.33f, phase * 0.6f ); + } else { + target.tint( 0.83f, 1.17f, 1.33f, 0.6f ); + } + } + + public void melt() { + + target.resetColor(); + killAndErase(); + + if (visible) { + Splash.at( target.center(), 0xFFB2D6FF, 5 ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + } + } + + public static IceBlock freeze( CharSprite sprite ) { + + IceBlock iceBlock = new IceBlock( sprite ); + sprite.parent.add( iceBlock ); + + return iceBlock; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Identification.java b/src/com/watabou/pixeldungeon/effects/Identification.java new file mode 100644 index 0000000000..cb91bcb5dd --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Identification.java @@ -0,0 +1,101 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.Group; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class Identification extends Group { + + private static int[] DOTS = { + -1, -3, + 0, -3, + +1, -3, + -1, -2, + +1, -2, + +1, -1, + 0, 0, + +1, 0, + 0, +1, + 0, +3 + }; + + public Identification( PointF p ) { + + for (int i=0; i < DOTS.length; i += 2) { + add( new Speck( p.x, p.y, DOTS[i], DOTS[i+1] ) ); + add( new Speck( p.x, p.y, DOTS[i], DOTS[i+1] ) ); + } + } + + @Override + public void update() { + super.update(); + if (countLiving() == 0) { + killAndErase(); + } + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } + + public static class Speck extends PixelParticle { + + public Speck( float x0, float y0, int mx, int my ) { + + super(); + color( 0x4488CC ); + + float x1 = x0 + mx * 3; + float y1 = y0 + my * 3; + + PointF p = new PointF().polar( Random.Float( 2 * PointF.PI ), 8 ); + x0 += p.x; + y0 += p.y; + + float dx = x1 - x0; + float dy = y1 - y0; + + x = x0; + y = y0; + speed.set( dx, dy ); + acc.set( -dx / 4, -dy / 4 ); + + left = lifespan = 2f; + } + + @Override + public void update() { + super.update(); + + am = 1 - Math.abs( left / lifespan - 0.5f ) * 2; + am *= am; + size( am * 2 ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Lightning.java b/src/com/watabou/pixeldungeon/effects/Lightning.java new file mode 100644 index 0000000000..f1ab3a1259 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Lightning.java @@ -0,0 +1,142 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public class Lightning extends Group { + + private static final float DURATION = 0.3f; + + private float life; + + private int length; + private float[] cx; + private float[] cy; + + private Image[] arcsS; + private Image[] arcsE; + + private Callback callback; + + public Lightning( int[] cells, int length, Callback callback ) { + + super(); + + this.callback = callback; + + Image proto = Effects.get( Effects.Type.LIGHTNING ); + float ox = 0; + float oy = proto.height / 2; + + this.length = length; + cx = new float[length]; + cy = new float[length]; + + for (int i=0; i < length; i++) { + int c = cells[i]; + cx[i] = (c % Level.WIDTH + 0.5f) * DungeonTilemap.SIZE; + cy[i] = (c / Level.WIDTH + 0.5f) * DungeonTilemap.SIZE; + } + + arcsS = new Image[length - 1]; + arcsE = new Image[length - 1]; + for (int i=0; i < length - 1; i++) { + + Image arc = arcsS[i] = new Image( proto ); + + arc.x = cx[i] - arc.origin.x; + arc.y = cy[i] - arc.origin.y; + arc.origin.set( ox, oy ); + add( arc ); + + arc = arcsE[i] = new Image( proto ); + arc.origin.set( ox, oy ); + add( arc ); + } + + life = DURATION; + + Sample.INSTANCE.play( Assets.SND_LIGHTNING ); + } + + private static final double A = 180 / Math.PI; + + @Override + public void update() { + super.update(); + + if ((life -= Game.elapsed) < 0) { + + killAndErase(); + if (callback != null) { + callback.call(); + } + + } else { + + float alpha = life / DURATION; + + for (int i=0; i < length - 1; i++) { + + float sx = cx[i]; + float sy = cy[i]; + float ex = cx[i+1]; + float ey = cy[i+1]; + + float x2 = (sx + ex) / 2 + Random.Float( -4, +4 ); + float y2 = (sy + ey) / 2 + Random.Float( -4, +4 ); + + float dx = x2 - sx; + float dy = y2 - sy; + Image arc = arcsS[i]; + arc.am = alpha; + arc.angle = (float)(Math.atan2( dy, dx ) * A); + arc.scale.x = (float)Math.sqrt( dx * dx + dy * dy ) / arc.width; + + dx = ex - x2; + dy = ey - y2; + arc = arcsE[i]; + arc.am = alpha; + arc.angle = (float)(Math.atan2( dy, dx ) * A); + arc.scale.x = (float)Math.sqrt( dx * dx + dy * dy ) / arc.width; + arc.x = x2 - arc.origin.x; + arc.y = y2 - arc.origin.x; + } + } + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } +} diff --git a/src/com/watabou/pixeldungeon/effects/MagicMissile.java b/src/com/watabou/pixeldungeon/effects/MagicMissile.java new file mode 100644 index 0000000000..9e938140e5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/MagicMissile.java @@ -0,0 +1,399 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.effects.particles.LeafParticle; +import com.watabou.pixeldungeon.effects.particles.PoisonParticle; +import com.watabou.pixeldungeon.effects.particles.PurpleParticle; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; +import com.watabou.pixeldungeon.effects.particles.WoolParticle; +import com.watabou.utils.Callback; +import com.watabou.utils.ColorMath; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class MagicMissile extends Emitter { + + private static final float SPEED = 200f; + + private Callback callback; + + private float sx; + private float sy; + private float time; + + public void reset( int from, int to, Callback callback ) { + this.callback = callback; + + revive(); + + PointF pf = DungeonTilemap.tileCenterToWorld( from ); + PointF pt = DungeonTilemap.tileCenterToWorld( to ); + + x = pf.x; + y = pf.y; + width = 0; + height = 0; + + PointF d = PointF.diff( pt, pf ); + PointF speed = new PointF( d ).normalize().scale( SPEED ); + sx = speed.x; + sy = speed.y; + time = d.length() / SPEED; + } + + public void size( float size ) { + x -= size / 2; + y -= size / 2; + width = height = size; + } + + public static void blueLight( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.pour( MagicParticle.FACTORY, 0.01f ); + } + + public static void fire( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 4 ); + missile.pour( FlameParticle.FACTORY, 0.01f ); + } + + public static void earth( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 2 ); + missile.pour( EarthParticle.FACTORY, 0.01f ); + } + + public static void purpleLight( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 2 ); + missile.pour( PurpleParticle.MISSILE, 0.01f ); + } + + public static void whiteLight( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 4 ); + missile.pour( WhiteParticle.FACTORY, 0.01f ); + } + + public static void wool( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 3 ); + missile.pour( WoolParticle.FACTORY, 0.01f ); + } + + public static void poison( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 3 ); + missile.pour( PoisonParticle.MISSILE, 0.01f ); + } + + public static void foliage( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 4 ); + missile.pour( LeafParticle.GENERAL, 0.01f ); + } + + public static void slowness( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.pour( SlowParticle.FACTORY, 0.01f ); + } + + public static void force( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 4 ); + missile.pour( ForceParticle.FACTORY, 0.01f ); + } + + public static void coldLight( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 4 ); + missile.pour( ColdParticle.FACTORY, 0.01f ); + } + + public static void shadow( Group group, int from, int to, Callback callback ) { + MagicMissile missile = ((MagicMissile)group.recycle( MagicMissile.class )); + missile.reset( from, to, callback ); + missile.size( 4 ); + missile.pour( ShadowParticle.MISSILE, 0.01f ); + } + + @Override + public void update() { + super.update(); + if (on) { + float d = Game.elapsed; + x += sx * d; + y += sy * d; + if ((time -= d) <= 0) { + on = false; + callback.call(); + } + } + } + + public static class MagicParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((MagicParticle)emitter.recycle( MagicParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public MagicParticle() { + super(); + + color( 0x88CCFF ); + lifespan = 0.5f; + + speed.set( Random.Float( -10, +10 ), Random.Float( -10, +10 ) ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + } + + @Override + public void update() { + super.update(); + // alpha: 1 -> 0; size: 1 -> 4 + size( 4 - (am = left / lifespan) * 3 ); + } + } + + public static class EarthParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((EarthParticle)emitter.recycle( EarthParticle.class )).reset( x, y ); + } + }; + + public EarthParticle() { + super(); + + lifespan = 0.5f; + + color( ColorMath.random( 0x555555, 0x777766 ) ); + + acc.set( 0, +40 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + size = 4; + + speed.set( Random.Float( -10, +10 ), Random.Float( -10, +10 ) ); + } + } + + public static class WhiteParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((WhiteParticle)emitter.recycle( WhiteParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public WhiteParticle() { + super(); + + lifespan = 0.4f; + + am = 0.5f; + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + } + + @Override + public void update() { + super.update(); + // size: 3 -> 0 + size( (left / lifespan) * 3 ); + } + } + + public static class SlowParticle extends PixelParticle { + + private Emitter emitter; + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((SlowParticle)emitter.recycle( SlowParticle.class )).reset( x, y, emitter ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public SlowParticle() { + super(); + + lifespan = 0.6f; + + color( 0x664422 ); + size( 2 ); + } + + public void reset( float x, float y, Emitter emitter ) { + revive(); + + this.x = x; + this.y = y; + this.emitter = emitter; + + left = lifespan; + + acc.set( 0 ); + speed.set( Random.Float( -20, +20 ), Random.Float( -20, +20 ) ); + } + + @Override + public void update() { + super.update(); + + am = left / lifespan; + acc.set( (emitter.x - x) * 10, (emitter.y - y) * 10 ); + } + } + + public static class ForceParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ForceParticle)emitter.recycle( ForceParticle.class )).reset( x, y ); + } + }; + + public ForceParticle() { + super(); + + lifespan = 0.6f; + + size( 4 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + acc.set( 0 ); + speed.set( Random.Float( -40, +40 ), Random.Float( -40, +40 ) ); + } + + @Override + public void update() { + super.update(); + + am = (left / lifespan) / 2; + acc.set( -speed.x * 10, -speed.y * 10 ); + } + } + + public static class ColdParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ColdParticle)emitter.recycle( ColdParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public ColdParticle() { + super(); + + lifespan = 0.6f; + + color( 0x2244FF ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + size = 8; + } + + @Override + public void update() { + super.update(); + + am = 1 - left / lifespan; + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Pushing.java b/src/com/watabou/pixeldungeon/effects/Pushing.java new file mode 100644 index 0000000000..7dfbb8406d --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Pushing.java @@ -0,0 +1,100 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Visual; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.utils.PointF; + +public class Pushing extends Actor { + + private CharSprite sprite; + private int from; + private int to; + + private Effect effect; + + public Pushing( Char ch, int from, int to ) { + sprite = ch.sprite; + this.from = from; + this.to = to; + } + + @Override + protected boolean act() { + if (sprite != null) { + + if (effect == null) { + new Effect(); + } + return false; + + } else { + + Actor.remove( Pushing.this ); + return true; + } + } + + public class Effect extends Visual { + + private static final float DELAY = 0.15f; + + private PointF end; + + private float delay; + + public Effect() { + super( 0, 0, 0, 0 ); + + point( sprite.worldToCamera( from ) ); + end = sprite.worldToCamera( to ); + + speed.set( 2 * (end.x - x) / DELAY, 2 * (end.y - y) / DELAY ); + acc.set( -speed.x / DELAY, -speed.y / DELAY ); + + delay = 0; + + sprite.parent.add( this ); + } + + @Override + public void update() { + super.update(); + + if ((delay += Game.elapsed) < DELAY) { + + sprite.x = x; + sprite.y = y; + + } else { + + sprite.point( end ); + + killAndErase(); + Actor.remove( Pushing.this ); + + next(); + } + } + } + +} diff --git a/src/com/watabou/pixeldungeon/effects/Ripple.java b/src/com/watabou/pixeldungeon/effects/Ripple.java new file mode 100644 index 0000000000..d45c8df266 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Ripple.java @@ -0,0 +1,59 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.levels.Level; + +public class Ripple extends Image { + + private static final float TIME_TO_FADE = 0.5f; + + private float time; + + public Ripple() { + super( Effects.get( Effects.Type.RIPPLE ) ); + } + + public void reset( int p ) { + revive(); + + x = (p % Level.WIDTH) * DungeonTilemap.SIZE; + y = (p / Level.WIDTH) * DungeonTilemap.SIZE; + + origin.set( width / 2, height / 2 ); + scale.set( 0 ); + + time = TIME_TO_FADE; + } + + @Override + public void update() { + super.update(); + + if ((time -= Game.elapsed) <= 0) { + kill(); + } else { + float p = time / TIME_TO_FADE; + scale.set( 1 - p ); + alpha( p ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Speck.java b/src/com/watabou/pixeldungeon/effects/Speck.java new file mode 100644 index 0000000000..0a3e5ffe49 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Speck.java @@ -0,0 +1,419 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import android.annotation.SuppressLint; +import android.util.FloatMath; +import android.util.SparseArray; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class Speck extends Image { + + public static final int HEALING = 0; + public static final int STAR = 1; + public static final int LIGHT = 2; + public static final int QUESTION = 3; + public static final int UP = 4; + public static final int SCREAM = 5; + public static final int BONE = 6; + public static final int WOOL = 7; + public static final int ROCK = 8; + public static final int NOTE = 9; + public static final int CHANGE = 10; + public static final int HEART = 11; + public static final int BUBBLE = 12; + public static final int STEAM = 13; + public static final int COIN = 14; + + public static final int DISCOVER = 101; + public static final int EVOKE = 102; + public static final int MASTERY = 103; + public static final int KIT = 104; + public static final int RATTLE = 105; + public static final int JET = 106; + public static final int TOXIC = 107; + public static final int PARALYSIS = 108; + public static final int DUST = 109; + public static final int FORGE = 110; + + private static final int SIZE = 7; + + private int type; + private float lifespan; + private float left; + + private static TextureFilm film; + + private static SparseArray factories = new SparseArray(); + + public Speck() { + super(); + + texture( Assets.SPECKS ); + if (film == null) { + film = new TextureFilm( texture, SIZE, SIZE ); + } + + origin.set( SIZE / 2f ); + } + + public void reset( int index, float x, float y, int type ) { + revive(); + + this.type = type; + switch (type) { + case DISCOVER: + frame( film.get( LIGHT ) ); + break; + case EVOKE: + case MASTERY: + case KIT: + case FORGE: + frame( film.get( STAR ) ); + break; + case RATTLE: + frame( film.get( BONE ) ); + break; + case JET: + case TOXIC: + case PARALYSIS: + case DUST: + frame( film.get( STEAM ) ); + break; + default: + frame( film.get( type ) ); + } + + this.x = x - origin.x; + this.y = y - origin.y; + + resetColor(); + scale.set( 1 ); + speed.set( 0 ); + acc.set( 0 ); + angle = 0; + angularSpeed = 0; + + switch (type) { + + case HEALING: + speed.set( 0, -20 ); + lifespan = 1f; + break; + + case STAR: + speed.polar( Random.Float( 2 * 3.1415926f ), Random.Float( 128 ) ); + acc.set( 0, 128 ); + angle = Random.Float( 360 ); + angularSpeed = Random.Float( -360, +360 ); + lifespan = 1f; + break; + + case FORGE: + speed.polar( Random.Float( -3.1415926f, 0 ), Random.Float( 64 ) ); + acc.set( 0, 128 ); + angle = Random.Float( 360 ); + angularSpeed = Random.Float( -360, +360 ); + lifespan = 0.51f; + break; + + case EVOKE: + speed.polar( Random.Float( -3.1415926f, 0 ), 50 ); + acc.set( 0, 50 ); + angle = Random.Float( 360 ); + angularSpeed = Random.Float( -180, +180 ); + lifespan = 1f; + break; + + case KIT: + speed.polar( index * 3.1415926f / 5, 50 ); + acc.set( -speed.x, -speed.y ); + angle = index * 36; + angularSpeed = 360; + lifespan = 1f; + break; + + case MASTERY: + speed.set( Random.Int( 2 ) == 0 ? Random.Float( -128, -64 ) : Random.Float( +64, +128 ), 0 ); + angularSpeed = speed.x < 0 ? -180 : +180; + acc.set( -speed.x, 0 ); + lifespan = 0.5f; + break; + + case LIGHT: + angle = Random.Float( 360 ); + angularSpeed = 90; + lifespan = 1f; + break; + + case DISCOVER: + angle = Random.Float( 360 ); + angularSpeed = 90; + lifespan = 0.5f; + am = 0; + break; + + case QUESTION: + lifespan = 0.8f; + break; + + case UP: + speed.set( 0, -20 ); + lifespan = 1f; + break; + + case SCREAM: + lifespan = 0.9f; + break; + + case BONE: + lifespan = 0.2f; + speed.polar( Random.Float( 2 * 3.1415926f ), 24 / lifespan ); + acc.set( 0, 128 ); + angle = Random.Float( 360 ); + angularSpeed = 360; + break; + + case RATTLE: + lifespan = 0.5f; + speed.set( 0, -200 ); + acc.set( 0, -2 * speed.y / lifespan ); + angle = Random.Float( 360 ); + angularSpeed = 360; + break; + + case WOOL: + lifespan = 0.5f; + speed.set( 0, -50 ); + angle = Random.Float( 360 ); + angularSpeed = Random.Float( -360, +360 ); + break; + + case ROCK: + angle = Random.Float( 360 ); + angularSpeed = Random.Float( -360, +360 ); + scale.set( Random.Float( 1, 2 ) ); + speed.set( 0, 64 ); + lifespan = 0.2f; + y -= speed.y * lifespan; + break; + + case NOTE: + angularSpeed = Random.Float( -30, +30 ); + speed.polar( (angularSpeed - 90) * PointF.G2R, 30 ); + lifespan = 1f; + break; + + case CHANGE: + angle = Random.Float( 360 ); + speed.polar( (angle - 90) * PointF.G2R, Random.Float( 4, 12 ) ); + lifespan = 1.5f; + break; + + case HEART: + speed.set( Random.Int( -10, +10 ), -40 ); + angularSpeed = Random.Float( -45, +45 ); + lifespan = 1f; + break; + + case BUBBLE: + speed.set( 0, -15 ); + scale.set( Random.Float( 0.8f, 1 ) ); + lifespan = Random.Float( 0.8f, 1.5f ); + break; + + case STEAM: + speed.y = -Random.Float( 20, 30 ); + angularSpeed = Random.Float( +180 ); + angle = Random.Float( 360 ); + lifespan = 1f; + break; + + case JET: + speed.y = +32; + acc.y = -64; + angularSpeed = Random.Float( 180, 360 ); + angle = Random.Float( 360 ); + lifespan = 0.5f; + break; + + case TOXIC: + hardlight( 0x50FF60 ); + angularSpeed = 30; + angle = Random.Float( 360 ); + lifespan = Random.Float( 1f, 3f ); + break; + + case PARALYSIS: + hardlight( 0xFFFF66 ); + angularSpeed = -30; + angle = Random.Float( 360 ); + lifespan = Random.Float( 1f, 3f ); + break; + + case DUST: + hardlight( 0xFFFF66 ); + angle = Random.Float( 360 ); + speed.polar( Random.Float( 2 * 3.1415926f ), Random.Float( 16, 48 ) ); + lifespan = 0.5f; + break; + + case COIN: + speed.polar( -PointF.PI * Random.Float( 0.3f, 0.7f ), Random.Float( 48, 96 ) ); + acc.y = 256; + lifespan = -speed.y / acc.y * 2; + break; + } + + left = lifespan; + } + + @SuppressLint("FloatMath") + @Override + public void update() { + super.update(); + + left -= Game.elapsed; + if (left <= 0) { + + kill(); + + } else { + + float p = 1 - left / lifespan; // 0 -> 1 + + switch (type) { + + case STAR: + case FORGE: + scale.set( 1 - p ); + am = p < 0.2f ? p * 5f : (1 - p) * 1.25f; + break; + + case KIT: + case MASTERY: + am = 1 - p * p; + break; + + case EVOKE: + + case HEALING: + am = p < 0.5f ? 1 : 2 - p * 2; + break; + + case LIGHT: + am = scale.set( p < 0.2f ? p * 5f : (1 - p) * 1.25f ).x; + break; + + case DISCOVER: + am = 1 - p; + scale.set( (p < 0.5f ? p : 1 - p) * 2 ); + break; + + case QUESTION: + scale.set( (float)(Math.sqrt( p < 0.5f ? p : 1 - p ) * 3) ); + break; + + case UP: + scale.set( (float)(Math.sqrt( p < 0.5f ? p : 1 - p ) * 2) ); + break; + + case SCREAM: + am = (float)Math.sqrt( (p < 0.5f ? p : 1 - p) * 2f ); + scale.set( p * 7 ); + break; + + case BONE: + case RATTLE: + am = p < 0.9f ? 1 : (1 - p) * 10; + break; + + case ROCK: + am = p < 0.2f ? p * 5 : 1 ; + break; + + case NOTE: + am = 1 - p * p; + break; + + case WOOL: + scale.set( 1 - p ); + break; + + case CHANGE: + am = (float)FloatMath.sqrt( (p < 0.5f ? p : 1 - p) * 2); + scale.y = (1 + p) * 0.5f; + scale.x = scale.y * FloatMath.cos( left * 15 ); + break; + + case HEART: + scale.set( 1 - p ); + am = 1 - p * p; + break; + + case BUBBLE: + am = p < 0.2f ? p * 5 : 1; + break; + + case STEAM: + case TOXIC: + case PARALYSIS: + case DUST: + am = p < 0.5f ? p : 1 - p; + scale.set( 1 + p * 2 ); + break; + + case JET: + am = (p < 0.5f ? p : 1 - p) * 2; + scale.set( p * 1.5f ); + break; + + case COIN: + scale.x = FloatMath.cos( left * 5 ); + rm = gm = bm = (Math.abs( scale.x ) + 1) * 0.5f; + am = p < 0.9f ? 1 : (1 - p) * 10; + break; + } + } + } + + public static Emitter.Factory factory( final int type ) { + + Emitter.Factory factory = factories.get( type ); + + if (factory == null) { + factory = new Emitter.Factory() { + @Override + public void emit ( Emitter emitter, int index, float x, float y ) { + Speck p = (Speck)emitter.recycle( Speck.class ); + p.reset( index, x, y, type ); + } + }; + factories.put( type, factory ); + } + + return factory; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/SpellSprite.java b/src/com/watabou/pixeldungeon/effects/SpellSprite.java new file mode 100644 index 0000000000..f27e1786f7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/SpellSprite.java @@ -0,0 +1,135 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import java.util.HashMap; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class SpellSprite extends Image { + + public static final int FOOD = 0; + public static final int MAP = 1; + public static final int CHARGE = 2; + public static final int MASTERY = 3; + + private static final int SIZE = 16; + + private enum Phase { + FADE_IN, STATIC, FADE_OUT + }; + + private static final float FADE_IN_TIME = 0.2f; + private static final float STATIC_TIME = 0.8f; + private static final float FADE_OUT_TIME = 0.4f; + + private static TextureFilm film; + + private Char target; + + private Phase phase; + private float duration; + private float passed; + + private static HashMap all = new HashMap(); + + public SpellSprite() { + super( Assets.SPELL_ICONS ); + + if (film == null) { + film = new TextureFilm( texture, SIZE ); + } + } + + public void reset( int index ) { + frame( film.get( index ) ); + origin.set( width / 2, height / 2 ); + + phase = Phase.FADE_IN; + + duration = FADE_IN_TIME; + passed = 0; + } + + @Override + public void update() { + super.update(); + + x = target.sprite.center().x - SIZE / 2; + y = target.sprite.y - SIZE; + + switch (phase) { + case FADE_IN: + alpha( passed / duration ); + scale.set( passed / duration ); + break; + case STATIC: + break; + case FADE_OUT: + alpha( 1 - passed / duration ); + break; + } + + if ((passed += Game.elapsed) > duration) { + switch (phase) { + case FADE_IN: + phase = Phase.STATIC; + duration = STATIC_TIME; + break; + case STATIC: + phase = Phase.FADE_OUT; + duration = FADE_OUT_TIME; + break; + case FADE_OUT: + kill(); + break; + } + + passed = 0; + } + } + + @Override + public void kill() { + super.kill(); + all.remove( target ); + } + + public static void show( Char ch, int index ) { + + if (!ch.sprite.visible) { + return; + } + + SpellSprite old = all.get( ch ); + if (old != null) { + old.kill(); + } + + SpellSprite sprite = GameScene.spellSprite(); + sprite.revive(); + sprite.reset( index ); + sprite.target = ch; + all.put( ch, sprite ); + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Splash.java b/src/com/watabou/pixeldungeon/effects/Splash.java new file mode 100644 index 0000000000..cf2d9ea821 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Splash.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class Splash { + + public static void at( int cell, final int color, int n ) { + at( DungeonTilemap.tileCenterToWorld( cell ), color, n ); + } + + public static void at( PointF p, final int color, int n ) { + + if (n <= 0) { + return; + } + + Emitter emitter = GameScene.emitter(); + emitter.pos( p ); + + FACTORY.color = color; + FACTORY.dir = -3.1415926f / 2; + FACTORY.cone = 3.1415926f; + emitter.burst( FACTORY, n ); + } + + public static void at( PointF p, final float dir, final float cone, final int color, int n ) { + + if (n <= 0) { + return; + } + + Emitter emitter = GameScene.emitter(); + emitter.pos( p ); + + FACTORY.color = color; + FACTORY.dir = dir; + FACTORY.cone = cone; + emitter.burst( FACTORY, n ); + } + + private static final SplashFactory FACTORY = new SplashFactory(); + + private static class SplashFactory extends Emitter.Factory { + + public int color; + public float dir; + public float cone; + + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + PixelParticle p = (PixelParticle)emitter.recycle( PixelParticle.Shrinking.class ); + + p.reset( x, y, color, 4, Random.Float( 0.5f, 1.0f ) ); + p.speed.polar( Random.Float( dir - cone / 2, dir + cone / 2 ), Random.Float( 40, 80 ) ); + p.acc.set( 0, +100 ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/effects/TorchHalo.java b/src/com/watabou/pixeldungeon/effects/TorchHalo.java new file mode 100644 index 0000000000..fe89081e63 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/TorchHalo.java @@ -0,0 +1,71 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.sprites.CharSprite; + +public class TorchHalo extends Halo { + + private CharSprite target; + + private float phase = 0; + + public TorchHalo( CharSprite sprite ) { + super( 24, 0xFFDDCC, 0.15f ); + target = sprite; + am = 0; + } + + @Override + public void update() { + super.update(); + + if (phase < 0) { + if ((phase += Game.elapsed) >= 0) { + killAndErase(); + } else { + scale.set( (2 + phase) * radius / RADIUS ); + am = -phase * brightness; + } + } else if (phase < 1) { + if ((phase += Game.elapsed) >= 1) { + phase = 1; + } + scale.set( phase * radius / RADIUS ); + am = phase * brightness; + } + + point( target.x + target.width / 2, target.y + target.height / 2 ); + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } + + public void putOut() { + phase = -1; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/Wound.java b/src/com/watabou/pixeldungeon/effects/Wound.java new file mode 100644 index 0000000000..6e4c60306d --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/Wound.java @@ -0,0 +1,83 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.levels.Level; + +public class Wound extends Image { + + private static final float TIME_TO_FADE = 0.8f; + + private float time; + + public Wound() { + super( Effects.get( Effects.Type.WOUND ) ); + origin.set( width / 2, height / 2 ); + } + + public void reset( int p ) { + revive(); + + x = (p % Level.WIDTH) * DungeonTilemap.SIZE + (DungeonTilemap.SIZE - width) / 2; + y = (p / Level.WIDTH) * DungeonTilemap.SIZE + (DungeonTilemap.SIZE - height) / 2; + + time = TIME_TO_FADE; + } + + @Override + public void update() { + super.update(); + + if ((time -= Game.elapsed) <= 0) { + kill(); + } else { + float p = time / TIME_TO_FADE; + alpha( p ); + scale.x = 1 + p; + } + } + + public static void hit( Char ch ) { + hit( ch, 0 ); + } + + public static void hit( Char ch, float angle ) { + Wound w = (Wound)ch.sprite.parent.recycle( Wound.class ); + ch.sprite.parent.bringToFront( w ); + w.reset( ch.pos ); + w.angle = angle; + } + + public static void hit( int pos ) { + hit( pos, 0 ); + } + + public static void hit( int pos, float angle ) { + Group parent = Dungeon.hero.sprite.parent; + Wound w = (Wound)parent.recycle( Wound.class ); + parent.bringToFront( w ); + w.reset( pos ); + w.angle = angle; + } +} diff --git a/src/com/watabou/pixeldungeon/effects/particles/BloodParticle.java b/src/com/watabou/pixeldungeon/effects/particles/BloodParticle.java new file mode 100644 index 0000000000..33b91e5c7d --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/BloodParticle.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; + +public class BloodParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((BloodParticle)emitter.recycle( BloodParticle.class )).reset( x, y ); + } + }; + + public BloodParticle() { + super(); + + color( 0xCC0000 ); + lifespan = 0.8f; + + acc.set( 0, +40 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + size = 4; + speed.set( 0 ); + } + + @Override + public void update() { + super.update(); + float p = left / lifespan; + am = p > 0.6f ? (1 - p) * 2.5f : 1; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/EarthParticle.java b/src/com/watabou/pixeldungeon/effects/particles/EarthParticle.java new file mode 100644 index 0000000000..1d1cb4a706 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/EarthParticle.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.ColorMath; +import com.watabou.utils.Random; + +public class EarthParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((EarthParticle)emitter.recycle( EarthParticle.class )).reset( x, y ); + } + }; + + public EarthParticle() { + super(); + + color( ColorMath.random( 0x444444, 0x777766 ) ); + angle = Random.Float( -30, 30 ); + + lifespan = 0.5f; + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + size( (p < 0.5f ? p : 1 - p) * 16 ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/ElmoParticle.java b/src/com/watabou/pixeldungeon/effects/particles/ElmoParticle.java new file mode 100644 index 0000000000..d87cf01125 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/ElmoParticle.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; + +public class ElmoParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ElmoParticle)emitter.recycle( ElmoParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public ElmoParticle() { + super(); + + color( 0x22EE66 ); + lifespan = 0.6f; + + acc.set( 0, -80 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + size = 4; + speed.set( 0 ); + } + + @Override + public void update() { + super.update(); + float p = left / lifespan; + am = p > 0.8f ? (1 - p) * 5 : 1; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/EnergyParticle.java b/src/com/watabou/pixeldungeon/effects/particles/EnergyParticle.java new file mode 100644 index 0000000000..31c97d5c61 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/EnergyParticle.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class EnergyParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((EnergyParticle)emitter.recycle( EnergyParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public EnergyParticle() { + super(); + + lifespan = 1f; + color( 0xFFFFAA ); + + speed.polar( Random.Float( 2 * PointF.PI ), Random.Float( 24, 32 ) ); + } + + public void reset( float x, float y ) { + revive(); + + left = lifespan; + + this.x = x - speed.x * lifespan; + this.y = y - speed.y * lifespan; + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + am = p < 0.5f ? p * p * 4 : (1 - p) * 2; + size( Random.Float( 5 * left / lifespan ) ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/FlameParticle.java b/src/com/watabou/pixeldungeon/effects/particles/FlameParticle.java new file mode 100644 index 0000000000..ae344f5d46 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/FlameParticle.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; + +public class FlameParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((FlameParticle)emitter.recycle( FlameParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public FlameParticle() { + super(); + + color( 0xEE7722 ); + lifespan = 0.6f; + + acc.set( 0, -80 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + size = 4; + speed.set( 0 ); + } + + @Override + public void update() { + super.update(); + float p = left / lifespan; + am = p > 0.8f ? (1 - p) * 5 : 1; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/FlowParticle.java b/src/com/watabou/pixeldungeon/effects/particles/FlowParticle.java new file mode 100644 index 0000000000..e62780075e --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/FlowParticle.java @@ -0,0 +1,109 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class FlowParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((FlowParticle)emitter.recycle( FlowParticle.class )).reset( x, y ); + } + }; + + public FlowParticle() { + super(); + + lifespan = 0.6f; + acc.set( 0, 32 ); + angularSpeed = Random.Float( -360, +360 ); + } + + public void reset( float x, float y ) { + revive(); + + left = lifespan; + + this.x = x; + this.y = y; + + am = 0; + size( 0 ); + speed.set( 0 ); + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + am = (p < 0.5f ? p : 1 - p) * 0.6f; + size( (1 - p) * 4 ); + } + + public static class Flow extends Group { + + private static final float DELAY = 0.1f; + + private int pos; + + private float x; + private float y; + + private float delay; + + public Flow( int pos ) { + super(); + + this.pos = pos; + + PointF p = DungeonTilemap.tileToWorld( pos ); + x = p.x; + y = p.y + DungeonTilemap.SIZE - 1; + + delay = Random.Float( DELAY ); + } + + @Override + public void update() { + + if (visible = Dungeon.visible[pos]) { + + super.update(); + + if ((delay -= Game.elapsed) <= 0) { + + delay = Random.Float( DELAY ); + + ((FlowParticle)recycle( FlowParticle.class )).reset( + x + Random.Float( DungeonTilemap.SIZE ), y ); + } + } + } + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/LeafParticle.java b/src/com/watabou/pixeldungeon/effects/particles/LeafParticle.java new file mode 100644 index 0000000000..a0112a417b --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/LeafParticle.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.utils.ColorMath; +import com.watabou.utils.Random; + +public class LeafParticle extends PixelParticle.Shrinking { + + public static int color1; + public static int color2; + + + public static final Emitter.Factory GENERAL = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + LeafParticle p = ((LeafParticle)emitter.recycle( LeafParticle.class )); + p.color( ColorMath.random( 0x004400, 0x88CC44 ) ); + p.reset( x, y ); + } + }; + + public static final Emitter.Factory LEVEL_SPECIFIC = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + LeafParticle p = ((LeafParticle)emitter.recycle( LeafParticle.class )); + p.color( ColorMath.random( Dungeon.level.color1, Dungeon.level.color2 ) ); + p.reset( x, y ); + } + }; + + public LeafParticle() { + super(); + + lifespan = 1.2f; + acc.set( 0, 25 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + speed.set( Random.Float( -8, +8 ), -20 ); + + left = lifespan; + size = Random.Float( 2, 3 ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/PoisonParticle.java b/src/com/watabou/pixeldungeon/effects/particles/PoisonParticle.java new file mode 100644 index 0000000000..16554f67a0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/PoisonParticle.java @@ -0,0 +1,88 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.ColorMath; +import com.watabou.utils.Random; + +public class PoisonParticle extends PixelParticle { + + public static final Emitter.Factory MISSILE = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((PoisonParticle)emitter.recycle( PoisonParticle.class )).resetMissile( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public static final Emitter.Factory SPLASH = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((PoisonParticle)emitter.recycle( PoisonParticle.class )).resetSplash( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public PoisonParticle() { + super(); + + lifespan = 0.6f; + + acc.set( 0, +30 ); + } + + public void resetMissile( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + speed.polar( Random.Float( 3.1415926f ), Random.Float( 6 ) ); + } + + public void resetSplash( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + speed.polar( Random.Float( 3.1415926f ), Random.Float( 10, 20 ) ); + } + + @Override + public void update() { + super.update(); + // alpha: 1 -> 0; size: 1 -> 4 + size( 4 - (am = left / lifespan) * 3 ); + // color: 0x8844FF -> 0x00FF00 + color( ColorMath.interpolate( 0x00FF00, 0x8844FF, am ) ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/PurpleParticle.java b/src/com/watabou/pixeldungeon/effects/particles/PurpleParticle.java new file mode 100644 index 0000000000..ca378fe525 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/PurpleParticle.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.ColorMath; +import com.watabou.utils.Random; + +public class PurpleParticle extends PixelParticle { + + public static final Emitter.Factory MISSILE = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((PurpleParticle)emitter.recycle( PurpleParticle.class )).reset( x, y ); + } + }; + + public static final Emitter.Factory BURST = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((PurpleParticle)emitter.recycle( PurpleParticle.class )).resetBurst( x, y ); + } + @Override + public boolean lightMode() { + return true; + } + }; + + public PurpleParticle() { + super(); + + lifespan = 0.5f; + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + speed.set( Random.Float( -5, +5 ), Random.Float( -5, +5 ) ); + + left = lifespan; + } + + public void resetBurst( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + speed.polar( Random.Float( 360 ), Random.Float( 16, 32 ) ); + + left = lifespan; + } + + @Override + public void update() { + super.update(); + // alpha: 1 -> 0; size: 1 -> 5 + size( 5 - (am = left / lifespan) * 4 ); + // color: 0xFF0044 -> 0x220066 + color( ColorMath.interpolate( 0x220066, 0xFF0044, am ) ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/ShadowParticle.java b/src/com/watabou/pixeldungeon/effects/particles/ShadowParticle.java new file mode 100644 index 0000000000..0df72649a2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/ShadowParticle.java @@ -0,0 +1,93 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.ColorMath; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class ShadowParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory MISSILE = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ShadowParticle)emitter.recycle( ShadowParticle.class )).reset( x, y ); + } + }; + + public static final Emitter.Factory CURSE = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ShadowParticle)emitter.recycle( ShadowParticle.class )).resetCurse( x, y ); + } + }; + + public static final Emitter.Factory UP = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ShadowParticle)emitter.recycle( ShadowParticle.class )).resetUp( x, y ); + } + }; + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + speed.set( Random.Float( -5, +5 ), Random.Float( -5, +5 ) ); + + size = 6; + left = lifespan = 0.5f; + } + + public void resetCurse( float x, float y ) { + revive(); + + size = 8; + left = lifespan = 0.5f; + + speed.polar( Random.Float( 2 * PointF.PI ), Random.Float( 16, 32 ) ); + this.x = x - speed.x * lifespan; + this.y = y - speed.y * lifespan; + } + + public void resetUp( float x, float y ) { + revive(); + + speed.set( Random.Float( -8, +8 ), Random.Float( -32, -48 ) ); + this.x = x; + this.y = y; + + size = 6; + left = lifespan = 1f; + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + // alpha: 0 -> 1 -> 0; size: 6 -> 0; color: 0x660044 -> 0x000000 + color( ColorMath.interpolate( 0x000000, 0x440044, p ) ); + am = p < 0.5f ? p * p * 4 : (1 - p) * 2; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/ShaftParticle.java b/src/com/watabou/pixeldungeon/effects/particles/ShaftParticle.java new file mode 100644 index 0000000000..ba4c2769d5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/ShaftParticle.java @@ -0,0 +1,66 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.Random; + +public class ShaftParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ShaftParticle)emitter.recycle( ShaftParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + } + }; + + public ShaftParticle() { + super(); + + lifespan = 1.2f; + speed.set( 0, -6 ); + } + + private float offs; + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + offs = -Random.Float( lifespan ); + left = lifespan - offs; + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + am = p < 0.5f ? p : 1 - p; + scale.x = (1 - p) * 4; + scale.y = 16 + (1 - p) * 16; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/SnowParticle.java b/src/com/watabou/pixeldungeon/effects/particles/SnowParticle.java new file mode 100644 index 0000000000..8b4b809ca7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/SnowParticle.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.Random; + +public class SnowParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((SnowParticle)emitter.recycle( SnowParticle.class )).reset( x, y ); + } + }; + + public SnowParticle() { + super(); + speed.set( 0, Random.Float( 5, 8 ) ); + lifespan = 1.2f; + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y - speed.y * lifespan; + + left = lifespan; + } + + @Override + public void update() { + super.update(); + float p = left / lifespan; + am = (p < 0.5f ? p : 1 - p) * 1.5f; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/SparkParticle.java b/src/com/watabou/pixeldungeon/effects/particles/SparkParticle.java new file mode 100644 index 0000000000..f817e432bb --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/SparkParticle.java @@ -0,0 +1,62 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.Random; + +public class SparkParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((SparkParticle)emitter.recycle( SparkParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return true; + }; + }; + + public SparkParticle() { + super(); + + size( 2 ); + + acc.set( 0, +50 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan = Random.Float( 0.5f, 1.0f ); + + speed.polar( Random.Float( 3.1415926f ), Random.Float( 20, 40 ) ); + } + + @Override + public void update() { + super.update(); + size( Random.Float( 5 * left / lifespan ) ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/WebParticle.java b/src/com/watabou/pixeldungeon/effects/particles/WebParticle.java new file mode 100644 index 0000000000..4b9ef61bb5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/WebParticle.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.Random; + +public class WebParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + for (int i=0; i < 3; i++) { + ((WebParticle)emitter.recycle( WebParticle.class )).reset( x, y ); + } + } + }; + + public WebParticle() { + super(); + + color( 0xCCCCCC ); + lifespan = 2f; + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + angle = Random.Float( 360 ); + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + am = p < 0.5f ? p : 1 - p; + scale.y = 16 + p * 8; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/WindParticle.java b/src/com/watabou/pixeldungeon/effects/particles/WindParticle.java new file mode 100644 index 0000000000..676fd29e53 --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/WindParticle.java @@ -0,0 +1,114 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class WindParticle extends PixelParticle { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((WindParticle)emitter.recycle( WindParticle.class )).reset( x, y ); + } + }; + + private static float angle = Random.Float( PointF.PI * 2 ); + private static PointF speed = new PointF().polar( angle, 5 ); + + private float size; + + public WindParticle() { + super(); + + lifespan = Random.Float( 1, 2 ); + scale.set( size = Random.Float( 3 ) ); + } + + public void reset( float x, float y ) { + revive(); + + left = lifespan; + + super.speed.set( WindParticle.speed ); + super.speed.scale( size ); + + this.x = x - super.speed.x * lifespan / 2; + this.y = y - super.speed.y * lifespan / 2; + + angle += Random.Float( -0.1f, +0.1f ); + speed = new PointF().polar( angle, 5 ); + + am = 0; + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + am = (p < 0.5f ? p : 1 - p) * size * 0.2f; + } + + public static class Wind extends Group { + + private int pos; + + private float x; + private float y; + + private float delay; + + public Wind( int pos ) { + super(); + + this.pos = pos; + PointF p = DungeonTilemap.tileToWorld( pos ); + x = p.x; + y = p.y; + + delay = Random.Float( 5 ); + } + + @Override + public void update() { + + if (visible = Dungeon.visible[pos]) { + + super.update(); + + if ((delay -= Game.elapsed) <= 0) { + + delay = Random.Float( 5 ); + + ((WindParticle)recycle( WindParticle.class )).reset( + x + Random.Float( DungeonTilemap.SIZE ), + y + Random.Float( DungeonTilemap.SIZE ) ); + } + } + } + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/effects/particles/WoolParticle.java b/src/com/watabou/pixeldungeon/effects/particles/WoolParticle.java new file mode 100644 index 0000000000..1361d962aa --- /dev/null +++ b/src/com/watabou/pixeldungeon/effects/particles/WoolParticle.java @@ -0,0 +1,54 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.ColorMath; +import com.watabou.utils.Random; + +public class WoolParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((WoolParticle)emitter.recycle( WoolParticle.class )).reset( x, y ); + } + }; + + public WoolParticle() { + super(); + + color( ColorMath.random( 0x999999, 0xEEEEE0 ) ); + + acc.set( 0, -40 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan = Random.Float( 0.6f, 1f ); + size = 5; + + speed.set( Random.Float( -10, +10 ), Random.Float( -10, +10 ) ); + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/items/Amulet.java b/src/com/watabou/pixeldungeon/items/Amulet.java new file mode 100644 index 0000000000..09be4e437a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Amulet.java @@ -0,0 +1,106 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.io.IOException; +import java.util.ArrayList; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.scenes.AmuletScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Amulet extends Item { + + private static final String AC_END = "END THE GAME"; + + { + name = "Amulet of Yendor"; + image = ItemSpriteSheet.AMULET; + + unique = true; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_END ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action == AC_END) { + + showAmuletScene( false ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean doPickUp( Hero hero ) { + if (super.doPickUp( hero )) { + + if (!Statistics.amuletObtained) { + Statistics.amuletObtained = true; + Dungeon.win( ResultDescriptions.WIN ); + Badges.validateVictory(); + + showAmuletScene( true ); + } + + return true; + } else { + return false; + } + } + + private void showAmuletScene( boolean showText ) { + try { + Dungeon.saveAll(); + AmuletScene.noText = !showText; + Game.switchScene( AmuletScene.class ); + } catch (IOException e) { + } + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public String info() { + return + "The Amulet of Yendor is the most powerful known artifact of unknown origin. It is said that the amulet " + + "is able to fulfil any wish if its owner's will-power is strong enough to \"persuade\" it to do it."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/Ankh.java b/src/com/watabou/pixeldungeon/items/Ankh.java new file mode 100644 index 0000000000..d7e94f08a1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Ankh.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Ankh extends Item { + + { + stackable = true; + name = "Ankh"; + image = ItemSpriteSheet.ANKH; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "The ancient symbol of immortality grants an ability to return to life after death. " + + "Upon resurrection all non-equipped items are lost."; + } + + @Override + public int price() { + return 50 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/ArmorKit.java b/src/com/watabou/pixeldungeon/items/ArmorKit.java new file mode 100644 index 0000000000..487de42bc1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/ArmorKit.java @@ -0,0 +1,124 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.ClassArmor; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.HeroSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag; + +public class ArmorKit extends Item { + + private static final String TXT_SELECT_ARMOR = "Select an armor to upgrade"; + private static final String TXT_UPGRADED = "you applied the armor kit to upgrade your %s"; + + private static final float TIME_TO_UPGRADE = 2; + + private static final String AC_APPLY = "APPLY"; + + { + name = "armor kit"; + image = ItemSpriteSheet.KIT; + + unique = true; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_APPLY ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action == AC_APPLY) { + + curUser = hero; + GameScene.selectItem( itemSelector, WndBag.Mode.ARMOR, TXT_SELECT_ARMOR ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + private void upgrade( Armor armor ) { + + detach( curUser.belongings.backpack ); + + curUser.sprite.centerEmitter().start( Speck.factory( Speck.KIT ), 0.05f, 10 ); + curUser.spend( TIME_TO_UPGRADE ); + curUser.busy(); + + GLog.w( TXT_UPGRADED, armor.name() ); + + ClassArmor classArmor = ClassArmor.upgrade( curUser, armor ); + if (curUser.belongings.armor == armor) { + + curUser.belongings.armor = classArmor; + ((HeroSprite)curUser.sprite).updateArmor(); + + } else { + + armor.detach( curUser.belongings.backpack ); + classArmor.collect( curUser.belongings.backpack ); + + } + + curUser.sprite.operate( curUser.pos ); + Sample.INSTANCE.play( Assets.SND_EVOKE ); + } + + @Override + public String info() { + return + "Using this kit of small tools and materials anybody can transform any armor into an \"epic armor\", " + + "which will keep all properties of the original armor, but will also provide its wearer a special ability " + + "depending on his class. No skills in tailoring, leatherworking or blacksmithing are required."; + } + + private final WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + ArmorKit.this.upgrade( (Armor)item ); + } + } + }; +} diff --git a/src/com/watabou/pixeldungeon/items/DewVial.java b/src/com/watabou/pixeldungeon/items/DewVial.java new file mode 100644 index 0000000000..57763345c8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/DewVial.java @@ -0,0 +1,187 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ShaftParticle; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; + +public class DewVial extends Item { + + private static final int MAX_VOLUME = 10; + + private static final String AC_DRINK = "DRINK"; + + private static final float TIME_TO_DRINK = 1f; + + private static final String TXT_VALUE = "%+dHP"; + private static final String TXT_STATUS = "%d/%d"; + + private static final String TXT_AUTO_DRINK = "The dew vial was emptied to heal your wounds."; + private static final String TXT_COLLECTED = "You collected a dewdrop into your dew vial."; + private static final String TXT_FULL = "Your dew vial is full!"; + private static final String TXT_EMPTY = "Your dew vial is empty!"; + + { + name = "dew vial"; + image = ItemSpriteSheet.VIAL; + + defaultAction = AC_DRINK; + + unique = true; + } + + private int volume = 0; + + private static final String VOLUME = "volume"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( VOLUME, volume ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + volume = bundle.getInt( VOLUME ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (volume > 0) { + actions.add( AC_DRINK ); + } + return actions; + } + + private static final double NUM = 20; + private static final double POW = Math.log10( NUM ); + + @Override + public void execute( final Hero hero, String action ) { + if (action.equals( AC_DRINK )) { + + if (volume > 0) { + + int value = (int)Math.ceil( Math.pow( volume, POW ) / NUM * hero.HT ); + int effect = Math.min( hero.HT - hero.HP, value ); + if (effect > 0) { + hero.HP += effect; + hero.sprite.emitter().burst( Speck.factory( Speck.HEALING ), volume > 5 ? 2 : 1 ); + hero.sprite.showStatus( CharSprite.POSITIVE, TXT_VALUE, effect ); + } + + volume = 0; + + hero.spend( TIME_TO_DRINK ); + hero.busy(); + + Sample.INSTANCE.play( Assets.SND_DRINK ); + hero.sprite.operate( hero.pos ); + + updateQuickslot(); + + } else { + GLog.w( TXT_EMPTY ); + } + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + public boolean isFull() { + return volume >= MAX_VOLUME; + } + + public void collectDew( Dewdrop dew ) { + + GLog.i( TXT_COLLECTED ); + volume += dew.quantity; + if (volume >= MAX_VOLUME) { + volume = MAX_VOLUME; + GLog.p( TXT_FULL ); + } + + updateQuickslot(); + } + + public void fill() { + volume = MAX_VOLUME; + updateQuickslot(); + } + + public static void autoDrink( Hero hero ) { + DewVial vial = hero.belongings.getItem( DewVial.class ); + if (vial != null && vial.isFull()) { + vial.execute( hero ); + hero.sprite.emitter().start( ShaftParticle.FACTORY, 0.2f, 3 ); + + GLog.w( TXT_AUTO_DRINK ); + } + } + + private static final Glowing WHITE = new Glowing( 0xFFFFCC ); + + @Override + public Glowing glowing() { + return isFull() ? WHITE : null; + } + + @Override + public String status() { + return Utils.format( TXT_STATUS, volume, MAX_VOLUME ); + } + + @Override + public String info() { + return + "You can store excess dew in this tiny vessel for drinking it later. " + + "If the vial is full, in a moment of deadly peril the dew will be " + + "consumed automatically."; + } + + @Override + public String toString() { + return super.toString() + " (" + status() + ")" ; + } +} diff --git a/src/com/watabou/pixeldungeon/items/Dewdrop.java b/src/com/watabou/pixeldungeon/items/Dewdrop.java new file mode 100644 index 0000000000..6bcc6f4e45 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Dewdrop.java @@ -0,0 +1,75 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Dewdrop extends Item { + + private static final String TXT_VALUE = "%+dHP"; + + { + name = "dewdrop"; + image = ItemSpriteSheet.DEWDROP; + + stackable = true; + } + + @Override + public boolean doPickUp( Hero hero ) { + + DewVial vial = hero.belongings.getItem( DewVial.class ); + + if (hero.HP < hero.HT || vial == null || vial.isFull()) { + + int value = 1 + (Dungeon.depth - 1) / 5; + if (hero.heroClass == HeroClass.HUNTRESS) { + value++; + } + + int effect = Math.min( hero.HT - hero.HP, value * quantity ); + if (effect > 0) { + hero.HP += effect; + hero.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + hero.sprite.showStatus( CharSprite.POSITIVE, TXT_VALUE, effect ); + } + + } else if (vial != null) { + + vial.collectDew( this ); + + } + + Sample.INSTANCE.play( Assets.SND_DEWDROP ); + hero.spendAndNext( TIME_TO_PICK_UP ); + + return true; + } + + @Override + public String info() { + return "A crystal clear dewdrop."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/EquipableItem.java b/src/com/watabou/pixeldungeon/items/EquipableItem.java new file mode 100644 index 0000000000..baa71ed1d5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/EquipableItem.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; + +public abstract class EquipableItem extends Item { + + public static final String AC_EQUIP = "EQUIP"; + public static final String AC_UNEQUIP = "UNEQUIP"; + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_EQUIP )) { + doEquip( hero ); + } else if (action.equals( AC_UNEQUIP )) { + doUnequip( hero, true ); + } else { + super.execute( hero, action ); + } + } + + @Override + public void doDrop( Hero hero ) { + if (!isEquipped( hero ) || doUnequip( hero, false )) { + super.doDrop( hero ); + } + } + + @Override + public void cast( final Hero user, int dst ) { + + if (isEquipped( user )) { + + if (quantity == 1 && !this.doUnequip( user, false )) { + return; + } + } + + super.cast( user, dst ); + } + + protected static void equipCursed( Hero hero ) { + hero.sprite.emitter().burst( ShadowParticle.CURSE, 6 ); + Sample.INSTANCE.play( Assets.SND_CURSED ); + } + + public abstract boolean doEquip( Hero hero ); + public abstract boolean doUnequip( Hero hero, boolean collect ); +} diff --git a/src/com/watabou/pixeldungeon/items/Generator.java b/src/com/watabou/pixeldungeon/items/Generator.java new file mode 100644 index 0000000000..31df0d6712 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Generator.java @@ -0,0 +1,263 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.HashMap; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Wandmaker.Rotberry; +import com.watabou.pixeldungeon.items.armor.*; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.items.food.Food; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.items.food.Pasty; +import com.watabou.pixeldungeon.items.potions.*; +import com.watabou.pixeldungeon.items.rings.*; +import com.watabou.pixeldungeon.items.scrolls.*; +import com.watabou.pixeldungeon.items.wands.*; +import com.watabou.pixeldungeon.items.weapon.*; +import com.watabou.pixeldungeon.items.weapon.melee.*; +import com.watabou.pixeldungeon.items.weapon.missiles.*; +import com.watabou.pixeldungeon.plants.*; +import com.watabou.utils.Random; + +public class Generator { + + public static enum Category { + WEAPON ( 15, Weapon.class ), + ARMOR ( 10, Armor.class ), + POTION ( 50, Potion.class ), + SCROLL ( 40, Scroll.class ), + WAND ( 4, Wand.class ), + RING ( 2, Ring.class ), + SEED ( 5, Plant.Seed.class ), + FOOD ( 0, Food.class ), + GOLD ( 50, Gold.class ); + + public Class[] classes; + public float[] probs; + + public float prob; + public Class superClass; + + private Category( float prob, Class superClass ) { + this.prob = prob; + this.superClass = superClass; + } + + public static int order( Item item ) { + for (int i=0; i < values().length; i++) { + if (values()[i].superClass.isInstance( item )) { + return i; + } + } + + return item instanceof Bag ? Integer.MAX_VALUE : Integer.MAX_VALUE - 1; + } + }; + + private static HashMap categoryProbs = new HashMap(); + + static { + + Category.GOLD.classes = new Class[]{ + Gold.class }; + Category.GOLD.probs = new float[]{ 1 }; + + Category.SCROLL.classes = new Class[]{ + ScrollOfIdentify.class, + ScrollOfTeleportation.class, + ScrollOfRemoveCurse.class, + ScrollOfUpgrade.class, + ScrollOfRecharging.class, + ScrollOfMagicMapping.class, + ScrollOfChallenge.class, + ScrollOfTerror.class, + ScrollOfLullaby.class, + ScrollOfWeaponUpgrade.class, + ScrollOfPsionicBlast.class, + ScrollOfMirrorImage.class }; + Category.SCROLL.probs = new float[]{ 30, 10, 15, 0, 10, 15, 12, 8, 8, 0, 4, 6 }; + + Category.POTION.classes = new Class[]{ + PotionOfHealing.class, + PotionOfExperience.class, + PotionOfToxicGas.class, + PotionOfParalyticGas.class, + PotionOfLiquidFlame.class, + PotionOfLevitation.class, + PotionOfStrength.class, + PotionOfMindVision.class, + PotionOfPurity.class, + PotionOfInvisibility.class, + PotionOfMight.class, + PotionOfFrost.class }; + Category.POTION.probs = new float[]{ 45, 4, 15, 10, 15, 10, 0, 20, 12, 10, 0, 10 }; + + Category.WAND.classes = new Class[]{ + WandOfTeleportation.class, + WandOfSlowness.class, + WandOfFirebolt.class, + WandOfRegrowth.class, + WandOfPoison.class, + WandOfBlink.class, + WandOfLightning.class, + WandOfAmok.class, + WandOfTelekinesis.class, + WandOfFlock.class, + WandOfMagicMissile.class, + WandOfDisintegration.class, + WandOfAvalanche.class }; + Category.WAND.probs = new float[]{ 10, 10, 15, 6, 10, 11, 15, 10, 6, 10, 0, 5, 5 }; + + Category.WEAPON.classes = new Class[]{ + Dagger.class, + Knuckles.class, + Quarterstaff.class, + Spear.class, + Mace.class, + Sword.class, + Longsword.class, + BattleAxe.class, + WarHammer.class, + Glaive.class, + ShortSword.class, + Dart.class, + Javelin.class, + IncendiaryDart.class, + CurareDart.class, + Shuriken.class, + Boomerang.class, + Tamahawk.class }; + Category.WEAPON.probs = new float[]{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1 }; + + Category.ARMOR.classes = new Class[]{ + ClothArmor.class, + LeatherArmor.class, + MailArmor.class, + ScaleArmor.class, + PlateArmor.class }; + Category.ARMOR.probs = new float[]{ 1, 1, 1, 1, 1 }; + + Category.FOOD.classes = new Class[]{ + Food.class, + Pasty.class, + MysteryMeat.class }; + Category.FOOD.probs = new float[]{ 4, 1, 0 }; + + Category.RING.classes = new Class[]{ + RingOfMending.class, + RingOfDetection.class, + RingOfShadows.class, + RingOfPower.class, + RingOfHerbalism.class, + RingOfAccuracy.class, + RingOfEvasion.class, + RingOfSatiety.class, + RingOfHaste.class, + RingOfElements.class, + RingOfHaggler.class, + RingOfThorns.class }; + Category.RING.probs = new float[]{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }; + + Category.SEED.classes = new Class[]{ + Firebloom.Seed.class, + Icecap.Seed.class, + Sorrowmoss.Seed.class, + Blindweed.Seed.class, + Sungrass.Seed.class, + Earthroot.Seed.class, + Fadeleaf.Seed.class, + Rotberry.Seed.class }; + Category.SEED.probs = new float[]{ 1, 1, 1, 1, 1, 1, 1, 0 }; + } + + public static void reset() { + for (Category cat : Category.values()) { + categoryProbs.put( cat, cat.prob ); + } + } + + public static Item random() { + return random( Random.chances( categoryProbs ) ); + } + + public static Item random( Category cat ) { + try { + + categoryProbs.put( cat, categoryProbs.get( cat ) / 2 ); + + switch (cat) { + case ARMOR: + return randomArmor(); + case WEAPON: + return randomWeapon(); + default: + return ((Item)cat.classes[Random.chances( cat.probs )].newInstance()).random(); + } + + } catch (Exception e) { + + return null; + + } + } + + public static Item random( Class cl ) { + try { + + return ((Item)cl.newInstance()).random(); + + } catch (Exception e) { + + return null; + + } + } + + public static Armor randomArmor() throws Exception { + + int curStr = Hero.STARTING_STR + Dungeon.potionOfStrength; + + Category cat = Category.ARMOR; + + Armor a1 = (Armor)cat.classes[Random.chances( cat.probs )].newInstance(); + Armor a2 = (Armor)cat.classes[Random.chances( cat.probs )].newInstance(); + + a1.random(); + a2.random(); + + return Math.abs( curStr - a1.STR ) < Math.abs( curStr - a2.STR ) ? a1 : a2; + } + + public static Weapon randomWeapon() throws Exception { + + int curStr = Hero.STARTING_STR + Dungeon.potionOfStrength; + + Category cat = Category.WEAPON; + + Weapon w1 = (Weapon)cat.classes[Random.chances( cat.probs )].newInstance(); + Weapon w2 = (Weapon)cat.classes[Random.chances( cat.probs )].newInstance(); + + w1.random(); + w2.random(); + + return Math.abs( curStr - w1.STR ) < Math.abs( curStr - w2.STR ) ? w1 : w2; + } +} diff --git a/src/com/watabou/pixeldungeon/items/Gold.java b/src/com/watabou/pixeldungeon/items/Gold.java new file mode 100644 index 0000000000..26ea0f86bc --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Gold.java @@ -0,0 +1,118 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Gold extends Item { + + private static final String TXT_COLLECT = "Collect gold coins to spend them later in a shop."; + private static final String TXT_INFO = "A pile of %d gold coins. " + TXT_COLLECT; + private static final String TXT_INFO_1 = "One gold coin. " + TXT_COLLECT; + private static final String TXT_VALUE = "%+d"; + + { + name = "gold"; + image = ItemSpriteSheet.GOLD; + stackable = true; + } + + public Gold() { + this( 1 ); + } + + public Gold( int value ) { + this.quantity = value; + } + + @Override + public ArrayList actions( Hero hero ) { + return new ArrayList(); + } + + @Override + public boolean doPickUp( Hero hero ) { + + Dungeon.gold += quantity; + Statistics.goldCollected += quantity; + Badges.validateGoldCollected(); + + GameScene.pickUp( this ); + hero.sprite.showStatus( CharSprite.NEUTRAL, TXT_VALUE, quantity ); + hero.spendAndNext( TIME_TO_PICK_UP ); + + Sample.INSTANCE.play( Assets.SND_GOLD, 1, 1, Random.Float( 0.9f, 1.1f ) ); + + return true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + switch (quantity) { + case 0: + return TXT_COLLECT; + case 1: + return TXT_INFO_1; + default: + return Utils.format( TXT_INFO, quantity ); + } + } + + @Override + public Item random() { + quantity = Random.Int( 20 + Dungeon.depth * 10, 40 + Dungeon.depth * 20 ); + return this; + } + + private static final String VALUE = "value"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( VALUE, quantity ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle(bundle); + quantity = bundle.getInt( VALUE ); + } +} diff --git a/src/com/watabou/pixeldungeon/items/Heap.java b/src/com/watabou/pixeldungeon/items/Heap.java new file mode 100644 index 0000000000..59325bcb07 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Heap.java @@ -0,0 +1,337 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.Collection; +import java.util.LinkedList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Wraith; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.Splash; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; +import com.watabou.pixeldungeon.items.food.ChargrilledMeat; +import com.watabou.pixeldungeon.items.food.FrozenCarpaccio; +import com.watabou.pixeldungeon.items.food.MysteryMeat; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.plants.Plant.Seed; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Heap implements Bundlable { + + private static final int SEEDS_TO_POTION = 3; + + public enum Type { + HEAP, + FOR_SALE, + CHEST, + LOCKED_CHEST, + CRYSTAL_CHEST, + TOMB, + SKELETON + } + public Type type = Type.HEAP; + + public int pos = 0; + + public ItemSprite sprite; + + protected LinkedList items = new LinkedList(); + + public int image() { + switch (type) { + case HEAP: + case FOR_SALE: + return size() > 0 ? items.peek().image() : 0; + case CHEST: + return ItemSpriteSheet.CHEST; + case LOCKED_CHEST: + return ItemSpriteSheet.LOCKED_CHEST; + case CRYSTAL_CHEST: + return ItemSpriteSheet.CRYSTAL_CHEST; + case TOMB: + return ItemSpriteSheet.TOMB; + case SKELETON: + return ItemSpriteSheet.BONES; + default: + return 0; + } + } + + public ItemSprite.Glowing glowing() { + return (type == Type.HEAP || type == Type.FOR_SALE) && items.size() > 0 ? items.peek().glowing() : null; + } + + public void open( Hero hero ) { + switch (type) { + case TOMB: + Wraith.spawnAround( hero.pos ); + break; + case SKELETON: + CellEmitter.center( pos ).start( Speck.factory( Speck.RATTLE ), 0.1f, 3 ); + for (Item item : items) { + if (item.cursed) { + if (Wraith.spawnAt( pos ) == null) { + hero.sprite.emitter().burst( ShadowParticle.CURSE, 6 ); + hero.damage( hero.HP / 2, this ); + } + Sample.INSTANCE.play( Assets.SND_CURSED ); + break; + } + } + break; + default: + } + + type = Type.HEAP; + sprite.link(); + sprite.drop(); + } + + public int size() { + return items.size(); + } + + public Item pickUp() { + + Item item = items.removeFirst(); + if (items.isEmpty()) { + destroy(); + } else if (sprite != null) { + sprite.view( image(), glowing() ); + } + + return item; + } + + public Item peek() { + return items.peek(); + } + + public void drop( Item item ) { + + if (item.stackable) { + + for (Item i : items) { + if (i.isSimilar( item )) { + i.quantity += item.quantity; + item = i; + break; + } + } + items.remove( item ); + + } + + if (item instanceof Dewdrop) { + items.add( item ); + } else { + items.addFirst( item ); + } + + if (sprite != null) { + sprite.view( image(), glowing() ); + } + } + + public void replace( Item a, Item b ) { + int index = items.indexOf( a ); + if (index != -1) { + items.remove( index ); + items.add( index, b ); + } + } + + public void burn() { + + if (type != Type.HEAP) { + return; + } + + boolean burnt = false; + boolean evaporated = false; + + for (Item item : items.toArray( new Item[0] )) { + if (item instanceof Scroll) { + items.remove( item ); + burnt = true; + } else if (item instanceof Dewdrop) { + items.remove( item ); + evaporated = true; + } else if (item instanceof MysteryMeat) { + replace( item, ChargrilledMeat.cook( (MysteryMeat)item ) ); + burnt = true; + } + } + + if (burnt || evaporated) { + + if (Dungeon.visible[pos]) { + if (burnt) { + burnFX( pos ); + } else { + evaporateFX( pos ); + } + } + + if (isEmpty()) { + destroy(); + } else if (sprite != null) { + sprite.view( image(), glowing() ); + } + + } + } + + public void freeze() { + + if (type != Type.HEAP) { + return; + } + + boolean frozen = false; + for (Item item : items.toArray( new Item[0] )) { + if (item instanceof MysteryMeat) { + replace( item, FrozenCarpaccio.cook( (MysteryMeat)item ) ); + frozen = true; + } + } + + if (frozen) { + if (isEmpty()) { + destroy(); + } else if (sprite != null) { + sprite.view( image(), glowing() ); + } + } + } + + public Item transmute() { + + CellEmitter.get( pos ).burst( Speck.factory( Speck.BUBBLE ), 3 ); + Splash.at( pos, 0xFFFFFF, 3 ); + + float chances[] = new float[items.size()]; + int count = 0; + + int index = 0; + for (Item item : items) { + if (item instanceof Seed) { + count += item.quantity; + chances[index++] = item.quantity; + } else { + count = 0; + break; + } + } + + if (count >= SEEDS_TO_POTION) { + + CellEmitter.get( pos ).burst( Speck.factory( Speck.WOOL ), 6 ); + Sample.INSTANCE.play( Assets.SND_PUFF ); + + if (Random.Int( count ) == 0) { + + CellEmitter.center( pos ).burst( Speck.factory( Speck.EVOKE ), 3 ); + + destroy(); + + Statistics.potionsCooked++; + Badges.validatePotionsCooked(); + + return Generator.random( Generator.Category.POTION ); + + } else { + + Seed proto = (Seed)items.get( Random.chances( chances ) ); + Class itemClass = proto.alchemyClass; + + destroy(); + + Statistics.potionsCooked++; + Badges.validatePotionsCooked(); + + if (itemClass == null) { + return Generator.random( Generator.Category.POTION ); + } else { + try { + return itemClass.newInstance(); + } catch (Exception e) { + return null; + } + } + } + + } else { + return null; + } + } + + public static void burnFX( int pos ) { + CellEmitter.get( pos ).burst( ElmoParticle.FACTORY, 6 ); + Sample.INSTANCE.play( Assets.SND_BURNING ); + } + + public static void evaporateFX( int pos ) { + CellEmitter.get( pos ).burst( Speck.factory( Speck.STEAM ), 5 ); + } + + public boolean isEmpty() { + return items == null || items.size() == 0; + } + + public void destroy() { + Dungeon.level.heaps.remove( this.pos ); + if (sprite != null) { + sprite.kill(); + } + items.clear(); + items = null; + } + + private static final String POS = "pos"; + private static final String TYPE = "type"; + private static final String ITEMS = "items"; + + @SuppressWarnings("unchecked") + @Override + public void restoreFromBundle( Bundle bundle ) { + pos = bundle.getInt( POS ); + type = Type.valueOf( bundle.getString( TYPE ) ); + items = new LinkedList( (Collection) bundle.getCollection( ITEMS ) ); + } + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( POS, pos ); + bundle.put( TYPE, type.toString() ); + bundle.put( ITEMS, items ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/Item.java b/src/com/watabou/pixeldungeon/items/Item.java new file mode 100644 index 0000000000..47d6c9328b --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Item.java @@ -0,0 +1,463 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.SnipersMark; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.items.weapon.missiles.MissileWeapon; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.CellSelector; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.MissileSprite; +import com.watabou.pixeldungeon.ui.QuickSlot; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Callback; + +public class Item implements Bundlable { + + private static final String TXT_PACK_FULL = "Your pack is too full for the %s"; + + private static final String TXT_TO_STRING = "%s"; + private static final String TXT_TO_STRING_X = "%s x%d"; + private static final String TXT_TO_STRING_LVL = "%s%+d"; + private static final String TXT_TO_STRING_LVL_X = "%s%+d x%d"; + + protected static final float TIME_TO_THROW = 1.0f; + protected static final float TIME_TO_PICK_UP = 1.0f; + protected static final float TIME_TO_DROP = 0.5f; + + public static final String AC_DROP = "DROP"; + public static final String AC_THROW = "THROW"; + + public String defaultAction; + + protected String name = "smth"; + protected int image = 0; + + public boolean stackable = false; + protected int quantity = 1; + + public int level = 0; + public boolean levelKnown = false; + + public boolean cursed; + public boolean cursedKnown; + + // Unique items persist through revival + public boolean unique = false; + + private static Comparator itemComparator = new Comparator() { + @Override + public int compare( Item lhs, Item rhs ) { + return Generator.Category.order( lhs ) - Generator.Category.order( rhs ); + } + }; + + public ArrayList actions( Hero hero ) { + ArrayList actions = new ArrayList(); + actions.add( AC_DROP ); + actions.add( AC_THROW ); + return actions; + } + + public boolean doPickUp( Hero hero ) { + if (collect( hero.belongings.backpack )) { + + GameScene.pickUp( this ); + Sample.INSTANCE.play( Assets.SND_ITEM ); + hero.spendAndNext( TIME_TO_PICK_UP ); + return true; + + } else { + return false; + } + } + + public void doDrop( Hero hero ) { + hero.spendAndNext( TIME_TO_DROP ); + Dungeon.level.drop( detachAll( hero.belongings.backpack ), hero.pos ).sprite.drop( hero.pos ); + } + + public void doThrow( Hero hero ) { + GameScene.selectCell( thrower ); + } + + public void execute( Hero hero, String action ) { + + curUser = hero; + curItem = this; + + if (action.equals( AC_DROP )) { + + doDrop( hero ); + + } else if (action.equals( AC_THROW )) { + + doThrow( hero ); + + } + } + + public void execute( Hero hero ) { + execute( hero, defaultAction ); + } + + protected void onThrow( int cell ) { + Heap heap = Dungeon.level.drop( this, cell ); + if (!heap.isEmpty()) { + heap.sprite.drop( cell ); + } + } + + public boolean collect( Bag container ) { + + ArrayList items = container.items; + + if (items.contains( this )) { + return true; + } + + for (Item item:items) { + if (item instanceof Bag && ((Bag)item).grab( this )) { + return collect( (Bag)item ); + } + } + + if (stackable) { + for (Item item:items) { + if (isSimilar( item )) { + item.quantity += quantity; + item.updateQuickslot(); + return true; + } + } + } + + if (items.size() < container.size) { + + if (Dungeon.hero != null && Dungeon.hero.isAlive()) { + Badges.validateItemLevelAquired( this ); + } + + items.add( this ); + QuickSlot.refresh(); + Collections.sort( items, itemComparator ); + return true; + + } else { + + GLog.n( TXT_PACK_FULL, name() ); + return false; + + } + } + + public boolean collect() { + return collect( Dungeon.hero.belongings.backpack ); + } + + public Item detach( Bag container ) { + + if (quantity <= 0) { + + return null; + + } else + if (quantity == 1) { + + return detachAll( container ); + + } else { + + quantity--; + updateQuickslot(); + + try { + return getClass().newInstance(); + } catch (Exception e) { + return null; + } + } + } + + public Item detachAll( Bag container ) { + for (Item item : container.items) { + if (item == this) { + container.items.remove( this ); + QuickSlot.refresh(); + return this; + } else if (item instanceof Bag) { + Bag bag = (Bag)item; + if (bag.contains( this )) { + detachAll( bag ); + return this; + } + } + } + + return this; + } + + public boolean isSimilar( Item item ) { + return getClass() == item.getClass(); + } + + public Item upgrade() { + + cursed = false; + cursedKnown = true; + this.level++; + + return this; + } + + public Item upgrade( int n ) { + for (int i=0; i < n; i++) { + upgrade(); + } + + return this; + } + + public Item degrade() { + + this.level--; + + return this; + } + + public Item degrade( int n ) { + for (int i=0; i < n; i++) { + degrade(); + } + + return this; + } + + public int visiblyUpgraded() { + return levelKnown ? level : 0; + } + + public boolean visiblyCursed() { + return cursed && cursedKnown; + } + + public boolean isUpgradable() { + return true; + } + + public boolean isIdentified() { + return levelKnown && cursedKnown; + } + + public boolean isEquipped( Hero hero ) { + return false; + } + + public Item identify() { + + levelKnown = true; + cursedKnown = true; + + return this; + } + + public static void evoke( Hero hero ) { + hero.sprite.emitter().burst( Speck.factory( Speck.EVOKE ), 5 ); + } + + @Override + public String toString() { + + if (levelKnown && level != 0) { + if (quantity > 1) { + return Utils.format( TXT_TO_STRING_LVL_X, name(), level, quantity ); + } else { + return Utils.format( TXT_TO_STRING_LVL, name(), level ); + } + } else { + if (quantity > 1) { + return Utils.format( TXT_TO_STRING_X, name(), quantity ); + } else { + return Utils.format( TXT_TO_STRING, name() ); + } + } + } + + public String name() { + return name; + } + + public final String trueName() { + return name; + } + + public int image() { + return image; + } + + public ItemSprite.Glowing glowing() { + return null; + } + + public String info() { + return desc(); + } + + public String desc() { + return ""; + } + + public int quantity() { + return quantity; + } + + public void quantity( int value ) { + quantity = value; + } + + public int price() { + return 0; + } + + public static Item virtual( Class cl ) { + try { + + Item item = (Item)cl.newInstance(); + item.quantity = 0; + return item; + + } catch (Exception e) { + return null; + } + } + + public Item random() { + return this; + } + + public String status() { + return quantity != 1 ? Integer.toString( quantity ) : null; + } + + public void updateQuickslot() { + if ((stackable && Dungeon.quickslot == getClass()) || Dungeon.quickslot == this) { + QuickSlot.refresh(); + } + } + + private static final String QUANTITY = "quantity"; + private static final String LEVEL = "level"; + private static final String LEVEL_KNOWN = "levelKnown"; + private static final String CURSED = "cursed"; + private static final String CURSED_KNOWN = "cursedKnown"; + private static final String QUICKSLOT = "quickslot"; + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( QUANTITY, quantity ); + bundle.put( LEVEL, level ); + bundle.put( LEVEL_KNOWN, levelKnown ); + bundle.put( CURSED, cursed ); + bundle.put( CURSED_KNOWN, cursedKnown ); + if (this == Dungeon.quickslot) { + bundle.put( QUICKSLOT, true ); + } + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + quantity = bundle.getInt( QUANTITY ); + levelKnown = bundle.getBoolean( LEVEL_KNOWN ); + cursedKnown = bundle.getBoolean( CURSED_KNOWN ); + + int level = bundle.getInt( LEVEL ); + if (level > 0) { + upgrade( level ); + } else if (level < 0) { + degrade( -level ); + } + + cursed = bundle.getBoolean( CURSED ); + + if (bundle.getBoolean( QUICKSLOT )) { + Dungeon.quickslot = this; + } + } + + public void cast( final Hero user, int dst ) { + + final int cell = Ballistica.cast( user.pos, dst, false, true ); + user.sprite.zap( cell ); + user.busy(); + + Char enemy = Actor.findChar( cell ); + QuickSlot.target( this, enemy ); + + float delay = TIME_TO_THROW; + if (this instanceof MissileWeapon) { + + // Refactoring needed! + delay *= ((MissileWeapon)this).speedFactor( user ); + if (enemy != null && enemy.buff( SnipersMark.class ) != null) { + delay *= 0.5f; + } + } + final float finalDelay = delay; + + ((MissileSprite)user.sprite.parent.recycle( MissileSprite.class )). + reset( user.pos, cell, this, new Callback() { + @Override + public void call() { + Item.this.detach( user.belongings.backpack ).onThrow( cell ); + user.spendAndNext( finalDelay ); + } + } ); + } + + protected static Hero curUser = null; + protected static Item curItem = null; + protected static CellSelector.Listener thrower = new CellSelector.Listener() { + @Override + public void onSelect( Integer target ) { + if (target != null) { + curItem.cast( curUser, target ); + } + } + @Override + public String prompt() { + return "Choose direction of throw"; + } + }; +} diff --git a/src/com/watabou/pixeldungeon/items/ItemStatusHandler.java b/src/com/watabou/pixeldungeon/items/ItemStatusHandler.java new file mode 100644 index 0000000000..ca3e41cd46 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/ItemStatusHandler.java @@ -0,0 +1,162 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class ItemStatusHandler { + + private Class[] items; + + private HashMap, Integer> images; + private HashMap, String> labels; + private HashSet> known; + + public ItemStatusHandler( Class[] items, String[] allLabels, Integer[] allImages ) { + + this.items = items; + + this.images = new HashMap, Integer>(); + this.labels = new HashMap, String>(); + known = new HashSet>(); + + ArrayList labelsLeft = new ArrayList( Arrays.asList( allLabels ) ); + ArrayList imagesLeft = new ArrayList( Arrays.asList( allImages ) ); + + for (int i=0; i < items.length; i++) { + + Class item = (Class)(items[i]); + + int index = Random.Int( labelsLeft.size() ); + + labels.put( item, labelsLeft.get( index ) ); + labelsLeft.remove( index ); + + images.put( item, imagesLeft.get( index ) ); + imagesLeft.remove( index ); + } + } + + public ItemStatusHandler( Class[] items, String[] labels, Integer[] images, Bundle bundle ) { + + this.items = items; + + this.images = new HashMap, Integer>(); + this.labels = new HashMap, String>(); + known = new HashSet>(); + + restore( bundle, labels, images ); + } + + private static final String PFX_IMAGE = "_image"; + private static final String PFX_LABEL = "_label"; + private static final String PFX_KNOWN = "_known"; + + public void save( Bundle bundle ) { + for (int i=0; i < items.length; i++) { + String itemName = items[i].toString(); + bundle.put( itemName + PFX_IMAGE, images.get( items[i] ) ); + bundle.put( itemName + PFX_LABEL, labels.get( items[i] ) ); + bundle.put( itemName + PFX_KNOWN, known.contains( items[i] ) ); + } + } + + private void restore( Bundle bundle, String[] allLabels, Integer[] allImages ) { + + ArrayList labelsLeft = new ArrayList( Arrays.asList( allLabels ) ); + ArrayList imagesLeft = new ArrayList( Arrays.asList( allImages ) ); + + for (int i=0; i < items.length; i++) { + + Class item = (Class)(items[i]); + String itemName = item.toString(); + + if (bundle.contains( itemName + PFX_LABEL )) { + + String label = bundle.getString( itemName + PFX_LABEL ); + labels.put( item, label ); + labelsLeft.remove( label ); + + Integer image = bundle.getInt( itemName + PFX_IMAGE ); + images.put( item, image ); + imagesLeft.remove( image ); + + if (bundle.getBoolean( itemName + PFX_KNOWN )) { + known.add( item ); + } + + } else { + + int index = Random.Int( labelsLeft.size() ); + + labels.put( item, labelsLeft.get( index ) ); + labelsLeft.remove( index ); + + images.put( item, imagesLeft.get( index ) ); + imagesLeft.remove( index ); + + } + } + } + + public int image( T item ) { + return images.get( item.getClass() ); + } + + public String label( T item ) { + return labels.get( item.getClass() ); + } + + public boolean isKnown( T item ) { + return known.contains( item.getClass() ); + } + + @SuppressWarnings("unchecked") + public void know( T item ) { + known.add( (Class)item.getClass() ); + + if (known.size() == items.length - 1) { + for (int i=0; i < items.length; i++) { + if (!known.contains( items[i] )) { + known.add( items[i] ); + break; + } + } + } + } + + public HashSet> known() { + return known; + } + + public HashSet> unknown() { + HashSet> result = new HashSet>(); + for (Class i : items) { + if (!known.contains( i )) { + result.add( i ); + } + } + return result; + } +} diff --git a/src/com/watabou/pixeldungeon/items/KindOfWeapon.java b/src/com/watabou/pixeldungeon/items/KindOfWeapon.java new file mode 100644 index 0000000000..ba651e1890 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/KindOfWeapon.java @@ -0,0 +1,115 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.ui.QuickSlot; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Random; + +public class KindOfWeapon extends EquipableItem { + + private static final String TXT_EQUIP_CURSED = "you wince as your grip involuntarily tightens around your %s"; + private static final String TXT_UNEQUIP_CURSED = "you can't remove cursed %s!"; + + protected static final float TIME_TO_EQUIP = 1f; + + public int MIN = 0; + public int MAX = 1; + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( isEquipped( hero ) ? AC_UNEQUIP : AC_EQUIP ); + return actions; + } + + @Override + public boolean isEquipped( Hero hero ) { + return hero.belongings.weapon == this; + } + + @Override + public boolean doEquip( Hero hero ) { + + detachAll( hero.belongings.backpack ); + + if (hero.belongings.weapon == null || hero.belongings.weapon.doUnequip( hero, true )) { + + hero.belongings.weapon = this; + activate( hero ); + + QuickSlot.refresh(); + + cursedKnown = true; + if (cursed) { + equipCursed( hero ); + GLog.n( TXT_EQUIP_CURSED, name() ); + } + + hero.spendAndNext( TIME_TO_EQUIP ); + return true; + + } else { + + collect( hero.belongings.backpack ); + return false; + } + } + + @Override + public boolean doUnequip( Hero hero, boolean collect ) { + + if (cursed) { + GLog.w( TXT_UNEQUIP_CURSED, name() ); + return false; + } + + hero.belongings.weapon = null; + hero.spendAndNext( TIME_TO_EQUIP ); + + if (collect && !collect( hero.belongings.backpack )) { + Dungeon.level.drop( this, hero.pos ); + } + + return true; + } + + public void activate( Hero hero ) { + } + + public int damageRoll( Hero owner ) { + return Random.NormalIntRange( MIN, MAX ); + } + + public float acuracyFactor( Hero hero ) { + return 1f; + } + + public float speedFactor( Hero hero ) { + return 1f; + } + + public void proc( Char attacker, Char defender, int damage ) { + } + +} diff --git a/src/com/watabou/pixeldungeon/items/LloydsBeacon.java b/src/com/watabou/pixeldungeon/items/LloydsBeacon.java new file mode 100644 index 0000000000..5be193943f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/LloydsBeacon.java @@ -0,0 +1,178 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.Game; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.InterlevelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; + +public class LloydsBeacon extends Item { + + private static final String TXT_PREVENTING = + "Strong magic aura of this place prevents you from using the lloyd's beacon!"; + + private static final String TXT_CREATURES = + "Psychic aura of neighbouring creatures doesn't allow you to use the lloyd's beacon at this moment."; + + private static final String TXT_RETURN = + "The lloyd's beacon is successfully set at your current location, now you can return here anytime."; + + private static final String TXT_INFO = + "Lloyd's beacon is an intricate magic device, that allows you to return to a place you have already been."; + + private static final String TXT_SET = + "\n\nThis beacon was set somewhere on the level %d of Pixel Dungeon."; + + public static final float TIME_TO_USE = 1; + + public static final String AC_SET = "SET"; + public static final String AC_RETURN = "RETURN"; + + private int returnDepth = -1; + private int returnPos; + + { + name = "lloyd's beacon"; + image = ItemSpriteSheet.BEACON; + + unique = true; + } + + private static final String DEPTH = "depth"; + private static final String POS = "pos"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( DEPTH, returnDepth ); + if (returnDepth != -1) { + bundle.put( POS, returnPos ); + } + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle(bundle); + returnDepth = bundle.getInt( DEPTH ); + returnPos = bundle.getInt( POS ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_SET ); + if (returnDepth != -1) { + actions.add( AC_RETURN ); + } + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + + if (action == AC_SET || action == AC_RETURN) { + + if (Dungeon.bossLevel()) { + hero.spend( LloydsBeacon.TIME_TO_USE ); + GLog.w( TXT_PREVENTING ); + return; + } + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + if (Actor.findChar( hero.pos + Level.NEIGHBOURS8[i] ) != null) { + GLog.w( TXT_CREATURES ); + return; + } + } + } + + if (action == AC_SET) { + + returnDepth = Dungeon.depth; + returnPos = hero.pos; + + hero.spend( LloydsBeacon.TIME_TO_USE ); + hero.busy(); + + hero.sprite.operate( hero.pos ); + Sample.INSTANCE.play( Assets.SND_BEACON ); + + GLog.i( TXT_RETURN ); + + } else if (action == AC_RETURN) { + + if (returnDepth == Dungeon.depth) { + reset(); + WandOfBlink.appear( hero, returnPos ); + Dungeon.level.press( returnPos, hero ); + Dungeon.observe(); + } else { + InterlevelScene.mode = InterlevelScene.Mode.RETURN; + InterlevelScene.returnDepth = returnDepth; + InterlevelScene.returnPos = returnPos; + reset(); + Game.switchScene( InterlevelScene.class ); + } + + + } else { + + super.execute( hero, action ); + + } + } + + public void reset() { + returnDepth = -1; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + private static final Glowing WHITE = new Glowing( 0xFFFFFF ); + + @Override + public Glowing glowing() { + return returnDepth != -1 ? WHITE : null; + } + + @Override + public String info() { + return TXT_INFO + (returnDepth == -1 ? "" : Utils.format( TXT_SET, returnDepth ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/items/Stylus.java b/src/com/watabou/pixeldungeon/items/Stylus.java new file mode 100644 index 0000000000..2ad363ff7f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Stylus.java @@ -0,0 +1,122 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.particles.PurpleParticle; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag; + +public class Stylus extends Item { + + private static final String TXT_SELECT_ARMOR = "Select an armor to inscribe on"; + private static final String TXT_INSCRIBED = "you inscribed the %s on your %s"; + + private static final float TIME_TO_INSCRIBE = 2; + + private static final String AC_INSCRIBE = "INSCRIBE"; + + { + name = "arcane stylus"; + image = ItemSpriteSheet.STYLUS; + + stackable = true; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_INSCRIBE ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action == AC_INSCRIBE) { + + curUser = hero; + GameScene.selectItem( itemSelector, WndBag.Mode.ARMOR, TXT_SELECT_ARMOR ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + private void inscribe( Armor armor ) { + + detach( curUser.belongings.backpack ); + + Class oldGlyphClass = armor.glyph != null ? armor.glyph.getClass() : null; + Armor.Glyph glyph = Armor.Glyph.random(); + while (glyph.getClass() == oldGlyphClass) { + glyph = Armor.Glyph.random(); + } + + GLog.w( TXT_INSCRIBED, glyph.name(), armor.name() ); + + armor.inscribe( glyph ); + + curUser.sprite.operate( curUser.pos ); + curUser.sprite.centerEmitter().start( PurpleParticle.BURST, 0.05f, 10 ); + Sample.INSTANCE.play( Assets.SND_BURNING ); + + curUser.spend( TIME_TO_INSCRIBE ); + curUser.busy(); + } + + @Override + public int price() { + return 50 * quantity; + } + + @Override + public String info() { + return + "This arcane stylus is made of some dark, very hard stone. Using it you can inscribe " + + "a magical glyph on your armor, but you have no power over choosing what glyph it will be, " + + "the stylus will decide it for you."; + } + + private final WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + Stylus.this.inscribe( (Armor)item ); + } + } + }; +} diff --git a/src/com/watabou/pixeldungeon/items/TomeOfMastery.java b/src/com/watabou/pixeldungeon/items/TomeOfMastery.java new file mode 100644 index 0000000000..6b27c0ba18 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/TomeOfMastery.java @@ -0,0 +1,145 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Fury; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.SpellSprite; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndChooseWay; + +public class TomeOfMastery extends Item { + + private static final String TXT_BLINDED = "You can't read while blinded"; + + public static final float TIME_TO_READ = 10; + + public static final String AC_READ = "READ"; + + { + stackable = false; + name = "Tome of Mastery"; + image = ItemSpriteSheet.MASTERY; + + unique = true; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_READ ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_READ )) { + + if (hero.buff( Blindness.class ) != null) { + GLog.w( TXT_BLINDED ); + return; + } + + curUser = hero; + + HeroSubClass way1 = null; + HeroSubClass way2 = null; + switch (hero.heroClass) { + case WARRIOR: + way1 = HeroSubClass.GLADIATOR; + way2 = HeroSubClass.BERSERKER; + break; + case MAGE: + way1 = HeroSubClass.BATTLEMAGE; + way2 = HeroSubClass.WARLOCK; + break; + case ROGUE: + way1 = HeroSubClass.FREERUNNER; + way2 = HeroSubClass.ASSASSIN; + break; + case HUNTRESS: + way1 = HeroSubClass.SNIPER; + way2 = HeroSubClass.WARDEN; + break; + } + GameScene.show( new WndChooseWay( this, way1, way2 ) ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean doPickUp( Hero hero ) { + Badges.validateMastery(); + return super.doPickUp( hero ); + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "This worn leather book is not that thick, but you feel somehow, " + + "that you can gather a lot from it. Remember though that reading " + + "this tome may require some time."; + } + + public void choose( HeroSubClass way ) { + + detach( curUser.belongings.backpack ); + + curUser.spend( TomeOfMastery.TIME_TO_READ ); + curUser.busy(); + + curUser.subClass = way; + + curUser.sprite.operate( curUser.pos ); + Sample.INSTANCE.play( Assets.SND_MASTERY ); + + SpellSprite.show( curUser, SpellSprite.MASTERY ); + curUser.sprite.emitter().burst( Speck.factory( Speck.MASTERY ), 12 ); + GLog.w( "You have chosen the way of the %s!", Utils.capitalize( way.title() ) ); + + if (way == HeroSubClass.BERSERKER && curUser.HP <= curUser.HT * Fury.LEVEL) { + Buff.affect( curUser, Fury.class ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/Torch.java b/src/com/watabou/pixeldungeon/items/Torch.java new file mode 100644 index 0000000000..d4b2104a21 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/Torch.java @@ -0,0 +1,94 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items; + +import java.util.ArrayList; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Light; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Torch extends Item { + + public static final String AC_LIGHT = "LIGHT"; + + public static final float TIME_TO_LIGHT = 1; + + { + name = "torch"; + image = ItemSpriteSheet.TORCH; + + stackable = true; + + defaultAction = AC_LIGHT; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_LIGHT ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + + if (action == AC_LIGHT) { + + hero.spend( TIME_TO_LIGHT ); + hero.busy(); + + hero.sprite.operate( hero.pos ); + + detach( hero.belongings.backpack ); + Buff.affect( hero, Light.class, Light.DURATION ); + + Emitter emitter = hero.sprite.centerEmitter(); + emitter.start( FlameParticle.FACTORY, 0.2f, 3 ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public int price() { + return 10 * quantity; + } + + @Override + public String info() { + return + "It's an indispensable item in The Demon Halls, which are notorious for their poor ambient lighting."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/Armor.java b/src/com/watabou/pixeldungeon/items/armor/Armor.java new file mode 100644 index 0000000000..2bebad28ab --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/Armor.java @@ -0,0 +1,376 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.EquipableItem; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.armor.glyphs.*; +import com.watabou.pixeldungeon.sprites.HeroSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Armor extends EquipableItem { + + private static final String TXT_EQUIP_CURSED = "your %s constricts around you painfully"; + private static final String TXT_UNEQUIP_CURSED = "You can't remove cursed %s!"; + + private static final String TXT_IDENTIFY = "you are now familiar enough with your %s to identify it. It is %s."; + + private static final String TXT_TO_STRING = "%s :%d"; + + private static final String TXT_INCOMPATIBLE = + "Interaction of different types of magic has erased the glyph on this armor!"; + + public int tier; + + public int STR; + public int DR; + + private int hitsToKnow = 10; + + public Glyph glyph; + + public Armor( int tier ) { + + this.tier = tier; + + STR = typicalSTR(); + DR = typicalDR(); + } + + private static final String GLYPH = "glyph"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( GLYPH, glyph ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + glyph = (Glyph)bundle.get( GLYPH ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( isEquipped( hero ) ? AC_UNEQUIP : AC_EQUIP ); + return actions; + } + + @Override + public boolean doEquip( Hero hero ) { + + detach( hero.belongings.backpack ); + + if (hero.belongings.armor == null || hero.belongings.armor.doUnequip( hero, true )) { + + hero.belongings.armor = this; + + cursedKnown = true; + if (cursed) { + equipCursed( hero ); + GLog.n( TXT_EQUIP_CURSED, toString() ); + } + + ((HeroSprite)hero.sprite).updateArmor(); + + hero.spendAndNext( 2 * hero.speed() ); + return true; + + } else { + + collect( hero.belongings.backpack ); + return false; + + } + } + + @Override + public boolean doUnequip( Hero hero, boolean collect ) { + if (cursed) { + + GLog.w( TXT_UNEQUIP_CURSED, name() ); + return false; + + } else { + + hero.belongings.armor = null; + hero.spendAndNext( hero.speed() ); + + ((HeroSprite)hero.sprite).updateArmor(); + + if (collect && !collect( hero.belongings.backpack )) { + Dungeon.level.drop( this, hero.pos ); + } + + return true; + + } + } + + @Override + public boolean isEquipped( Hero hero ) { + return hero.belongings.armor == this; + } + + @Override + public Item upgrade() { + return upgrade( false ); + } + + public Item upgrade( boolean inscribe ) { + + if (glyph != null) { + if (!inscribe && Random.Int( level ) > 0) { + GLog.w( TXT_INCOMPATIBLE ); + inscribe( null ); + } + } else { + if (inscribe) { + inscribe( Glyph.random() ); + } + }; + + DR += tier; + STR--; + + return super.upgrade(); + } + + public Item safeUpgrade() { + return upgrade( glyph != null ); + } + + @Override + public Item degrade() { + DR -= tier; + STR++; + + return super.degrade(); + } + + public int proc( Char attacker, Char defender, int damage ) { + + if (glyph != null) { + damage = glyph.proc( this, attacker, defender, damage ); + } + + if (!levelKnown) { + if (--hitsToKnow <= 0) { + levelKnown = true; + GLog.w( TXT_IDENTIFY, name(), toString() ); + Badges.validateItemLevelAquired( this ); + } + } + + return damage; + } + + @Override + public String toString() { + return levelKnown ? Utils.format( TXT_TO_STRING, super.toString(), STR ) : super.toString(); + } + + @Override + public String name() { + return glyph == null ? super.name() : glyph.name( super.name() ); + } + + @Override + public String info() { + String name = name(); + StringBuilder info = new StringBuilder( desc() ); + + if (levelKnown) { + info.append( + "\n\nThis " + name + " provides damage absorption up to " + + "" + Math.max( DR, 0 ) + " points per attack. " ); + + if (STR > Dungeon.hero.STR()) { + + if (isEquipped( Dungeon.hero )) { + info.append( + "\n\nBecause of your inadequate strength your " + + "movement speed and defense skill is decreased. " ); + } else { + info.append( + "\n\nBecause of your inadequate strength wearing this armor " + + "will decrease your movement speed and defense skill. " ); + } + + } + } else { + info.append( + "\n\nTypical " + name + " provides damage absorption up to " + typicalDR() + " points per attack " + + " and requires " + typicalSTR() + " points of strength. " ); + if (typicalSTR() > Dungeon.hero.STR()) { + info.append( "Probably this armor is too heavy for you. " ); + } + } + + if (glyph != null) { + info.append( "It is inscribed." ); + } + + if (isEquipped( Dungeon.hero )) { + info.append( "\n\nYou are wearing the " + name + + (cursed ? ", and because it is cursed, you are powerless to remove it." : ".") ); + } else { + if (cursedKnown && cursed) { + info.append( "\n\nYou can feel a malevolent magic lurking within the " + name + "." ); + } + } + + return info.toString(); + } + + @Override + public Item random() { + if (Random.Float() < 0.4) { + int n = 1; + if (Random.Int( 3 ) == 0) { + n++; + if (Random.Int( 3 ) == 0) { + n++; + } + } + if (Random.Int( 2 ) == 0) { + upgrade( n ); + } else { + degrade( n ); + cursed = true; + } + } + + if (Random.Int( 10 ) == 0) { + inscribe( Glyph.random() ); + } + + return this; + } + + public int typicalSTR() { + return 7 + tier * 2; + } + + public int typicalDR() { + return tier * 2; + } + + @Override + public int price() { + int price = 10 * (1 << (tier - 1)); + if (glyph != null) { + price *= 1.5; + } + if (cursed && cursedKnown) { + price /= 2; + } + if (levelKnown) { + if (level > 0) { + price *= (level + 1); + } else if (level < 0) { + price /= (1 - level); + } + } + if (price < 1) { + price = 1; + } + return price; + } + + public Armor inscribe( Glyph glyph ) { + this.glyph = glyph; + return this; + } + + public boolean isInscribed() { + return glyph != null; + } + + @Override + public ItemSprite.Glowing glowing() { + return glyph != null ? glyph.glowing() : null; + } + + public static abstract class Glyph implements Bundlable { + + private static final Class[] glyphs = new Class[]{ + Bounce.class, Affection.class, AntiEntropy.class, Multiplicity.class, + Potential.class, Metabolism.class, Stench.class, Viscosity.class, + Displacement.class, Entanglement.class }; + + private static final float[] chances= new float[]{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + + public abstract int proc( Armor armor, Char attacker, Char defender, int damage ); + + public String name() { + return name( "glyph" ); + } + + public String name( String armorName ) { + return armorName; + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + } + + @Override + public void storeInBundle( Bundle bundle ) { + } + + public ItemSprite.Glowing glowing() { + return ItemSprite.Glowing.WHITE; + } + + public boolean checkOwner( Char owner ) { + if (!owner.isAlive() && owner instanceof Hero) { + + ((Hero)owner).killerGlyph = this; + Badges.validateDeathFromGlyph(); + return true; + + } else { + return false; + } + } + + @SuppressWarnings("unchecked") + public static Glyph random() { + try { + return ((Class)glyphs[ Random.chances( chances ) ]).newInstance(); + } catch (Exception e) { + return null; + } + } + + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/ClassArmor.java b/src/com/watabou/pixeldungeon/items/armor/ClassArmor.java new file mode 100644 index 0000000000..0baebc821e --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/ClassArmor.java @@ -0,0 +1,134 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; + +abstract public class ClassArmor extends Armor { + + private static final String TXT_LOW_HEALTH = "Your health is too low!"; + private static final String TXT_NOT_EQUIPPED = "You need to be wearing this armor to use its special power!"; + + { + levelKnown = true; + cursedKnown = true; + defaultAction = special(); + } + + public ClassArmor() { + super( 6 ); + } + + public static ClassArmor upgrade ( Hero owner, Armor armor ) { + + ClassArmor classArmor = null; + + switch (owner.heroClass) { + case WARRIOR: + classArmor = new WarriorArmor(); + break; + case ROGUE: + classArmor = new RogueArmor(); + break; + case MAGE: + classArmor = new MageArmor(); + break; + case HUNTRESS: + classArmor = new HuntressArmor(); + break; + } + + classArmor.STR = armor.STR; + classArmor.DR = armor.DR; + + classArmor.inscribe( armor.glyph ); + + return classArmor; + } + + private static final String ARMOR_STR = "STR"; + private static final String ARMOR_DR = "DR"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( ARMOR_STR, STR ); + bundle.put( ARMOR_DR, DR ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + STR = bundle.getInt( ARMOR_STR ); + DR = bundle.getInt( ARMOR_DR ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (hero.HP >= 2 && isEquipped( hero )) { + actions.add( special() ); + } + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action == special()) { + + if (hero.HP < 2) { + GLog.w( TXT_LOW_HEALTH ); + } else if (!isEquipped( hero )) { + GLog.w( TXT_NOT_EQUIPPED ); + } else { + curUser = hero; + doSpecial(); + } + + } else { + super.execute( hero, action ); + } + } + + abstract public String special(); + abstract public void doSpecial(); + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public int price() { + return 0; + } + + @Override + public String desc() { + return "The thing looks awesome!"; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/ClothArmor.java b/src/com/watabou/pixeldungeon/items/armor/ClothArmor.java new file mode 100644 index 0000000000..182bb0d42e --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/ClothArmor.java @@ -0,0 +1,38 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + + +public class ClothArmor extends Armor { + + { + name = "cloth armor"; + image = ItemSpriteSheet.ARMOR_CLOTH; + } + + public ClothArmor() { + super( 1 ); + } + + @Override + public String desc() { + return "This lightweight armor offers basic protection."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/HuntressArmor.java b/src/com/watabou/pixeldungeon/items/armor/HuntressArmor.java new file mode 100644 index 0000000000..7135d7e5a6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/HuntressArmor.java @@ -0,0 +1,107 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import java.util.HashMap; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.missiles.Shuriken; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.sprites.MissileSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class HuntressArmor extends ClassArmor { + + private static final String TXT_NO_ENEMIES = "No enemies in sight"; + private static final String TXT_NOT_HUNTRESS = "Only huntresses can use this armor!"; + + private static final String AC_SPECIAL = "SPECTRAL BLADES"; + + { + name = "huntress cloak"; + image = ItemSpriteSheet.ARMOR_HUNTRESS; + } + + private HashMap targets = new HashMap(); + + @Override + public String special() { + return AC_SPECIAL; + } + + @Override + public void doSpecial() { + + Item proto = new Shuriken(); + + for (Mob mob : Dungeon.level.mobs) { + if (Level.fieldOfView[mob.pos]) { + + Callback callback = new Callback() { + @Override + public void call() { + curUser.attack( targets.get( this ) ); + targets.remove( this ); + if (targets.isEmpty()) { + curUser.spendAndNext( curUser.attackDelay() ); + } + } + }; + + ((MissileSprite)curUser.sprite.parent.recycle( MissileSprite.class )). + reset( curUser.pos, mob.pos, proto, callback ); + + targets.put( callback, mob ); + } + } + + if (targets.size() == 0) { + GLog.w( TXT_NO_ENEMIES ); + return; + } + + curUser.HP /= 2; + + curUser.sprite.zap( curUser.pos ); + curUser.busy(); + } + + @Override + public boolean doEquip( Hero hero ) { + if (hero.heroClass == HeroClass.HUNTRESS) { + return super.doEquip( hero ); + } else { + GLog.w( TXT_NOT_HUNTRESS ); + return false; + } + } + + @Override + public String desc() { + return + "A huntress in such cloak can create a fan of spectral blades. Each of these blades " + + "will target a single enemy in the huntress's field of view, inflicting damage depending " + + "on her currently equipped melee weapon."; + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/items/armor/LeatherArmor.java b/src/com/watabou/pixeldungeon/items/armor/LeatherArmor.java new file mode 100644 index 0000000000..e4e53437ec --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/LeatherArmor.java @@ -0,0 +1,38 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + + +public class LeatherArmor extends Armor { + + { + name = "leather armor"; + image = ItemSpriteSheet.ARMOR_LEATHER; + } + + public LeatherArmor() { + super( 2 ); + } + + @Override + public String desc() { + return "Armor made from tanned monster hide. Not as light as cloth armor but provides better protection."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/MageArmor.java b/src/com/watabou/pixeldungeon/items/armor/MageArmor.java new file mode 100644 index 0000000000..cadc05bf5f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/MageArmor.java @@ -0,0 +1,87 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; + +public class MageArmor extends ClassArmor { + + private static final String AC_SPECIAL = "MOLTEN EARTH"; + + private static final String TXT_NOT_MAGE = "Only mages can use this armor!"; + + { + name = "mage robe"; + image = ItemSpriteSheet.ARMOR_MAGE; + } + + @Override + public String special() { + return AC_SPECIAL; + } + + @Override + public String desc() { + return + "Wearing this gorgeous robe, a mage can cast a spell of molten earth: all the enemies " + + "in his field of view will be set on fire and unable to move at the same time."; + } + + @Override + public void doSpecial() { + + for (Mob mob : Dungeon.level.mobs) { + if (Level.fieldOfView[mob.pos]) { + Buff.affect( mob, Burning.class ).reignite( mob ); + Buff.prolong( mob, Roots.class, 3 ); + } + } + + curUser.HP /= 2; + + curUser.spend( Actor.TICK ); + curUser.sprite.operate( curUser.pos ); + curUser.busy(); + + curUser.sprite.centerEmitter().start( ElmoParticle.FACTORY, 0.15f, 4 ); + Sample.INSTANCE.play( Assets.SND_READ ); + } + + @Override + public boolean doEquip( Hero hero ) { + if (hero.heroClass == HeroClass.MAGE) { + return super.doEquip( hero ); + } else { + GLog.w( TXT_NOT_MAGE ); + return false; + } + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/items/armor/MailArmor.java b/src/com/watabou/pixeldungeon/items/armor/MailArmor.java new file mode 100644 index 0000000000..1039c5fff9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/MailArmor.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + + +public class MailArmor extends Armor { + + { + name = "mail armor"; + image = ItemSpriteSheet.ARMOR_MAIL; + } + + public MailArmor() { + super( 3 ); + } + + @Override + public String desc() { + return + "Interlocking metal links make for a tough but flexible suit of armor."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/PlateArmor.java b/src/com/watabou/pixeldungeon/items/armor/PlateArmor.java new file mode 100644 index 0000000000..c69822b61c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/PlateArmor.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + + +public class PlateArmor extends Armor { + + { + name = "plate armor"; + image = ItemSpriteSheet.ARMOR_PLATE; + } + + public PlateArmor() { + super( 5 ); + } + + @Override + public String desc() { + return + "Enormous plates of metal are joined together into a suit that provides " + + "unmatched protection to any adventurer strong enough to bear its staggering weight."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/RogueArmor.java b/src/com/watabou/pixeldungeon/items/armor/RogueArmor.java new file mode 100644 index 0000000000..fe75a1e851 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/RogueArmor.java @@ -0,0 +1,117 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.actors.mobs.Mob.State; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.CellSelector; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; + +public class RogueArmor extends ClassArmor { + + private static final String TXT_FOV = "You can only jump to an empty location in your field of view"; + private static final String TXT_NOT_ROGUE = "Only rogues can use this armor!"; + + private static final String AC_SPECIAL = "SMOKE BOMB"; + + { + name = "rogue garb"; + image = ItemSpriteSheet.ARMOR_ROGUE; + } + + @Override + public String special() { + return AC_SPECIAL; + } + + @Override + public void doSpecial() { + GameScene.selectCell( teleporter ); + } + + @Override + public boolean doEquip( Hero hero ) { + if (hero.heroClass == HeroClass.ROGUE) { + return super.doEquip( hero ); + } else { + GLog.w( TXT_NOT_ROGUE ); + return false; + } + } + + @Override + public String desc() { + return + "Wearing this dark garb, a rogue can perform a trick, that is called \"smoke bomb\" " + + "(though no real explosives are used): he blinds enemies who could see him and jumps aside."; + } + + protected static CellSelector.Listener teleporter = new CellSelector.Listener() { + + @Override + public void onSelect( Integer target ) { + if (target != null) { + + if (!Level.fieldOfView[target] || + !(Level.passable[target] || Level.avoid[target]) || + Actor.findChar( target ) != null) { + + GLog.w( TXT_FOV ); + return; + } + + curUser.HP /= 2; + + for (Mob mob : Dungeon.level.mobs) { + if (Level.fieldOfView[mob.pos]) { + Buff.prolong( mob, Blindness.class, 2 ); + mob.state = State.WANDERING; + mob.sprite.emitter().burst( Speck.factory( Speck.LIGHT ), 4 ); + } + } + + WandOfBlink.appear( curUser, target ); + CellEmitter.get( target ).burst( Speck.factory( Speck.WOOL ), 10 ); + Sample.INSTANCE.play( Assets.SND_PUFF ); + Dungeon.level.press( target, curUser ); + Dungeon.observe(); + + curUser.spendAndNext( Actor.TICK ); + } + } + + @Override + public String prompt() { + return "Choose a location to jump to"; + } + }; +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/items/armor/ScaleArmor.java b/src/com/watabou/pixeldungeon/items/armor/ScaleArmor.java new file mode 100644 index 0000000000..5d46e31c96 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/ScaleArmor.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + + +public class ScaleArmor extends Armor { + + { + name = "scale armor"; + image = ItemSpriteSheet.ARMOR_SCALE; + } + + public ScaleArmor() { + super( 4 ); + } + + @Override + public String desc() { + return + "The metal scales sewn onto a leather vest create a flexible, yet protective armor."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/WarriorArmor.java b/src/com/watabou/pixeldungeon/items/armor/WarriorArmor.java new file mode 100644 index 0000000000..8988a8d668 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/WarriorArmor.java @@ -0,0 +1,129 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.tweeners.PosTweener; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Fury; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.CellSelector; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.PointF; + +public class WarriorArmor extends ClassArmor { + + private static int LEAP_TIME = 1; + private static int SHOCK_TIME = 3; + + private static final String AC_SPECIAL = "HEROIC LEAP"; + + private static final String TXT_NOT_WARRIOR = "Only warriors can use this armor!"; + + { + name = "warrior suit of armor"; + image = ItemSpriteSheet.ARMOR_WARRIOR; + } + + @Override + public String special() { + return AC_SPECIAL; + } + + @Override + public void doSpecial() { + GameScene.selectCell( leaper ); + } + + @Override + public boolean doEquip( Hero hero ) { + if (hero.heroClass == HeroClass.WARRIOR) { + return super.doEquip( hero ); + } else { + GLog.w( TXT_NOT_WARRIOR ); + return false; + } + } + + @Override + public String desc() { + return + "While this armor looks heavy, it allows a warrior to perform heroic leap towards " + + "a targeted location, slamming down to stun all neighbouring enemies."; + } + + protected static CellSelector.Listener leaper = new CellSelector.Listener() { + + @Override + public void onSelect( Integer target ) { + if (target != null && target != curUser.pos) { + + int cell = Ballistica.cast( curUser.pos, target, false, true ); + if (Actor.findChar( cell ) != null && cell != curUser.pos) { + cell = Ballistica.trace[Ballistica.distance - 2]; + } + + curUser.HP /= 2; + if (curUser.subClass == HeroSubClass.BERSERKER && curUser.HP <= curUser.HT * Fury.LEVEL) { + Buff.affect( curUser, Fury.class ); + } + + Invisibility.dispel(); + + curUser.move( cell ); + curUser.sprite.place( cell ); + Dungeon.level.press( target, curUser ); + Dungeon.observe(); + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + Char mob = Actor.findChar( curUser.pos + Level.NEIGHBOURS8[i] ); + if (mob != null && mob != curUser) { + Buff.prolong( mob, Paralysis.class, SHOCK_TIME ); + } + } + + PointF pos = curUser.sprite.point(); + Camera.main.target = null; + curUser.sprite.y -= 16; + curUser.sprite.parent.add( new PosTweener( curUser.sprite, pos, 0.1f ) ); + + CellEmitter.center( cell ).burst( Speck.factory( Speck.DUST ), 10 ); + + curUser.spendAndNext( LEAP_TIME ); + } + } + + @Override + public String prompt() { + return "Choose direction to leap"; + } + }; +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Affection.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Affection.java new file mode 100644 index 0000000000..7e62d96538 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Affection.java @@ -0,0 +1,66 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Charm; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.GameMath; +import com.watabou.utils.Random; + +public class Affection extends Glyph { + + private static final String TXT_AFFECTION = "%s of affection"; + + private static ItemSprite.Glowing PINK = new ItemSprite.Glowing( 0xFF4488 ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = (int)GameMath.gate( 0, armor.level, 6 ); + + if (Level.adjacent( attacker.pos, defender.pos ) && Random.Int( level / 2 + 5 ) >= 4) { + + int duration = Random.IntRange( 2, 5 ); + + Buff.affect( attacker, Charm.class, Charm.durationFactor( attacker ) * duration ); + attacker.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 ); + + Buff.affect( defender, Charm.class, Random.Float( Charm.durationFactor( defender ) * duration / 2, duration ) ); + defender.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 ); + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_AFFECTION, weaponName ); + } + + @Override + public Glowing glowing() { + return PINK; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/AntiEntropy.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/AntiEntropy.java new file mode 100644 index 0000000000..86a4283a15 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/AntiEntropy.java @@ -0,0 +1,67 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Frost; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.effects.particles.SnowParticle; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class AntiEntropy extends Glyph { + + private static final String TXT_ANTI_ENTROPY = "%s of anti-entropy"; + + private static ItemSprite.Glowing BLUE = new ItemSprite.Glowing( 0x0000FF ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = Math.max( 0, armor.level ); + + if (Level.adjacent( attacker.pos, defender.pos ) && Random.Int( level + 6 ) >= 5) { + + Buff.prolong( attacker, Frost.class, Frost.duration( attacker ) * Random.Float( 1f, 1.5f )); + CellEmitter.get( attacker.pos ).start( SnowParticle.FACTORY, 0.2f, 6 ); + + Buff.affect( defender, Burning.class ).reignite( defender ); + defender.sprite.emitter().burst( FlameParticle.FACTORY, 5 ); + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_ANTI_ENTROPY, weaponName ); + } + + @Override + public Glowing glowing() { + return BLUE; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Bounce.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Bounce.java new file mode 100644 index 0000000000..241da599b8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Bounce.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.Pushing; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Random; + +public class Bounce extends Glyph { + + private static final String TXT_BOUNCE = "%s of bounce"; + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = Math.max( 0, armor.level ); + + if (Level.adjacent( attacker.pos, defender.pos ) && Random.Int( level + 5) >= 4) { + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + int ofs = Level.NEIGHBOURS8[i]; + if (attacker.pos - defender.pos == ofs) { + int newPos = attacker.pos + ofs; + if ((Level.passable[newPos] || Level.avoid[newPos]) && Actor.findChar( newPos ) == null) { + + Actor.addDelayed( new Pushing( attacker, attacker.pos, newPos ), -1 ); + + attacker.pos = newPos; + // Âîò ýòî âîò êðèâî :( + if (attacker instanceof Mob) { + Dungeon.level.mobPress( (Mob)attacker ); + } else { + Dungeon.level.press( newPos, attacker ); + } + + } + break; + } + } + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_BOUNCE, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Displacement.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Displacement.java new file mode 100644 index 0000000000..6174244272 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Displacement.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Displacement extends Glyph { + + private static final String TXT_DISPLACEMENT = "%s of displacement"; + + private static ItemSprite.Glowing BLUE = new ItemSprite.Glowing( 0x66AAFF ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage ) { + + if (Dungeon.bossLevel()) { + return damage; + } + + int nTries = (armor.level < 0 ? 1 : armor.level + 1) * 5; + for (int i=0; i < nTries; i++) { + int pos = Random.Int( Level.LENGTH ); + if (Dungeon.visible[pos] && Level.passable[pos] && Actor.findChar( pos ) == null) { + + WandOfBlink.appear( defender, pos ); + Dungeon.level.press( pos, defender ); + Dungeon.observe(); + + break; + } + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_DISPLACEMENT, weaponName ); + } + + @Override + public Glowing glowing() { + return BLUE; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Entanglement.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Entanglement.java new file mode 100644 index 0000000000..e41652b354 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Entanglement.java @@ -0,0 +1,66 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.EarthParticle; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.plants.Earthroot; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Entanglement extends Glyph { + + private static final String TXT_ENTANGLEMENT = "%s of entanglement"; + + private static ItemSprite.Glowing GREEN = new ItemSprite.Glowing( 0x448822 ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage ) { + + int level = Math.max( 0, armor.level ); + + if (Random.Int( 4 ) == 0) { + + Buff.prolong( defender, Roots.class, 5 - level / 5 ); + Buff.affect( defender, Earthroot.Armor.class ).level( 5 * (level + 1) ); + CellEmitter.bottom( defender.pos ).start( EarthParticle.FACTORY, 0.05f, 8 ); + Camera.main.shake( 1, 0.4f ); + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_ENTANGLEMENT, weaponName ); + } + + @Override + public Glowing glowing() { + return GREEN; + } + +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Metabolism.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Metabolism.java new file mode 100644 index 0000000000..7555f231f1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Metabolism.java @@ -0,0 +1,74 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.utils.Random; + +public class Metabolism extends Glyph { + + private static final String TXT_METABOLISM = "%s of metabolism"; + + private static ItemSprite.Glowing RED = new ItemSprite.Glowing( 0xCC0000 ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = Math.max( 0, armor.level ); + if (Random.Int( level / 2 + 5 ) >= 4) { + + int healing = Math.min( defender.HT - defender.HP, Random.Int( 1, defender.HT / 5 ) ); + + if (healing > 0) { + + Hunger hunger = defender.buff( Hunger.class ); + + if (hunger != null && !hunger.isStarving()) { + + hunger.satisfy( -Hunger.STARVING / 10 ); + BuffIndicator.refreshHero(); + + defender.HP += healing; + defender.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + defender.sprite.showStatus( CharSprite.POSITIVE, Integer.toString( healing ) ); + } + } + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_METABOLISM, weaponName ); + } + + @Override + public Glowing glowing() { + return RED; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Multiplicity.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Multiplicity.java new file mode 100644 index 0000000000..9ce857fc73 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Multiplicity.java @@ -0,0 +1,81 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.MirrorImage; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Multiplicity extends Glyph { + + private static final String TXT_MULTIPLICITY = "%s of multiplicity"; + + private static ItemSprite.Glowing PINK = new ItemSprite.Glowing( 0xCCAA88 ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = Math.max( 0, armor.level ); + + if (Random.Int( level / 2 + 6 ) >= 5) { + + ArrayList respawnPoints = new ArrayList(); + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + int p = defender.pos + Level.NEIGHBOURS8[i]; + if (Actor.findChar( p ) == null && (Level.passable[p] || Level.avoid[p])) { + respawnPoints.add( p ); + } + } + + if (respawnPoints.size() > 0) { + MirrorImage mob = new MirrorImage(); + mob.duplicate( (Hero)defender ); + GameScene.add( mob ); + WandOfBlink.appear( mob, Random.element( respawnPoints ) ); + + defender.damage( Random.IntRange( 1, defender.HT / 6 ), /*attacker*/ this ); + checkOwner( defender ); + } + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_MULTIPLICITY, weaponName ); + } + + @Override + public Glowing glowing() { + return PINK; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Potential.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Potential.java new file mode 100644 index 0000000000..331cfb5492 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Potential.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.Lightning; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.traps.LightningTrap; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Potential extends Glyph { + + private static final String TXT_POTENTIAL = "%s of potential"; + + private static ItemSprite.Glowing BLUE = new ItemSprite.Glowing( 0x66CCEE ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = Math.max( 0, armor.level ); + + if (Level.adjacent( attacker.pos, defender.pos ) && Random.Int( level + 7 ) >= 6) { + + int dmg = Random.IntRange( 1, damage ); + attacker.damage( dmg, LightningTrap.LIGHTNING ); + dmg = Random.IntRange( 1, dmg ); + defender.damage( dmg, LightningTrap.LIGHTNING ); + + checkOwner( defender ); + if (defender == Dungeon.hero) { + Camera.main.shake( 2, 0.3f ); + } + + int[] points = {attacker.pos, defender.pos}; + attacker.sprite.parent.add( new Lightning( points, 2, null ) ); + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_POTENTIAL, weaponName ); + } + + @Override + public Glowing glowing() { + return BLUE; + } +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Stench.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Stench.java new file mode 100644 index 0000000000..af2e29a938 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Stench.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Stench extends Glyph { + + private static final String TXT_STENCH = "%s of stench"; + + private static ItemSprite.Glowing GREEN = new ItemSprite.Glowing( 0x22CC44 ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage) { + + int level = Math.max( 0, armor.level ); + + if (Level.adjacent( attacker.pos, defender.pos ) && Random.Int( level + 5 ) >= 4) { + + GameScene.add( Blob.seed( attacker.pos, 20, ToxicGas.class ) ); + + } + + return damage; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_STENCH, weaponName ); + } + + @Override + public Glowing glowing() { + return GREEN; + } + +} diff --git a/src/com/watabou/pixeldungeon/items/armor/glyphs/Viscosity.java b/src/com/watabou/pixeldungeon/items/armor/glyphs/Viscosity.java new file mode 100644 index 0000000000..ae8253d55d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/armor/glyphs/Viscosity.java @@ -0,0 +1,150 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.armor.glyphs; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.armor.Armor.Glyph; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Viscosity extends Glyph { + + private static final String TXT_VISCOSITY = "%s of viscosity"; + + private static ItemSprite.Glowing PURPLE = new ItemSprite.Glowing( 0x8844CC ); + + @Override + public int proc( Armor armor, Char attacker, Char defender, int damage ) { + + if (damage == 0) { + return 0; + } + + int level = Math.max( 0, armor.level ); + + if (Random.Int( level + 7 ) >= 6) { + + DeferedDamage debuff = defender.buff( DeferedDamage.class ); + if (debuff == null) { + debuff = new DeferedDamage(); + debuff.attachTo( defender ); + } + debuff.prolong( damage ); + + defender.sprite.showStatus( CharSprite.WARNING, "deferred %d", damage ); + + return 0; + + } else { + return damage; + } + } + + @Override + public String name( String weaponName) { + return String.format( TXT_VISCOSITY, weaponName ); + } + + @Override + public Glowing glowing() { + return PURPLE; + } + + public static class DeferedDamage extends Buff { + + protected int damage = 0; + + private static final String DAMAGE = "damage"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( DAMAGE, damage ); + + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + damage = bundle.getInt( DAMAGE ); + } + + @Override + public boolean attachTo( Char target ) { + if (super.attachTo( target )) { + postpone( TICK ); + return true; + } else { + return false; + } + } + + public void prolong( int damage ) { + this.damage += damage; + }; + + @Override + public int icon() { + return BuffIndicator.DEFERRED; + } + + @Override + public String toString() { + return Utils.format( "Defered damage (%d)", damage ); + } + + @Override + public boolean act() { + if (target.isAlive()) { + + target.damage( 1, this ); + if (target == Dungeon.hero && !target.isAlive()) { + // Refactoring needed! + Glyph glyph = new Viscosity(); + Dungeon.fail( Utils.format( ResultDescriptions.GLYPH, glyph.name(), Dungeon.depth ) ); + GLog.n( "%s killed you...", glyph.name() ); + + Badges.validateDeathFromGlyph(); + } + spend( TICK ); + + if (--damage <= 0) { + detach(); + } + + } else { + + detach(); + + } + + return true; + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/bags/Bag.java b/src/com/watabou/pixeldungeon/items/bags/Bag.java new file mode 100644 index 0000000000..fcd2d2e21a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/bags/Bag.java @@ -0,0 +1,186 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.bags; + +import java.util.ArrayList; +import java.util.Iterator; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.windows.WndBag; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; + +public class Bag extends Item implements Iterable { + + public static final String AC_OPEN = "OPEN"; + + { + image = 11; + + defaultAction = AC_OPEN; + } + + public Char owner; + + public ArrayList items = new ArrayList(); + + public int size = 1; + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_OPEN )) { + + GameScene.show( new WndBag( this, null, WndBag.Mode.ALL, null ) ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean collect( Bag container ) { + if (super.collect( container )) { + + owner = container.owner; + + for (Item item : container.items.toArray( new Item[0] )) { + if (grab( item )) { + item.detachAll( container ); + item.collect( this ); + } + } + + Badges.validateAllBagsBought( this ); + + return true; + } else { + return false; + } + } + + @Override + public Item detach( Bag container ) { + owner = null; + return super.detach( container ); + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + public void clear() { + items.clear(); + } + + private static final String ITEMS = "inventory"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( ITEMS, items ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + for (Bundlable item : bundle.getCollection( ITEMS )) { + ((Item)item).collect( this ); + }; + } + + public boolean contains( Item item ) { + for (Item i : items) { + if (i == item) { + return true; + } else if (i instanceof Bag && ((Bag)i).contains( item )) { + return true; + } + } + return false; + } + + public boolean grab( Item item ) { + return false; + } + + @Override + public Iterator iterator() { + return new ItemIterator(); + } + + private class ItemIterator implements Iterator { + + private int index = 0; + private Iterator nested = null; + + @Override + public boolean hasNext() { + if (nested != null) { + return nested.hasNext() || index < items.size(); + } else { + return index < items.size(); + } + } + + @Override + public Item next() { + if (nested != null && nested.hasNext()) { + + return nested.next(); + + } else { + + nested = null; + + Item item = items.get( index++ ); + if (item instanceof Bag) { + nested = ((Bag)item).iterator(); + } + + return item; + } + } + + @Override + public void remove() { + if (nested != null) { + nested.remove(); + } else { + items.remove( index ); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/bags/ScrollHolder.java b/src/com/watabou/pixeldungeon/items/bags/ScrollHolder.java new file mode 100644 index 0000000000..0bef891de0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/bags/ScrollHolder.java @@ -0,0 +1,49 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.bags; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class ScrollHolder extends Bag { + + { + name = "scroll holder"; + image = ItemSpriteSheet.HOLDER; + + size = 12; + } + + @Override + public boolean grab( Item item ) { + return item instanceof Scroll; + } + + @Override + public int price() { + return 50; + } + + @Override + public String info() { + return + "You can place any number of scrolls into this tubular container. " + + "It saves room in your backpack and protects scrolls from fire."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/bags/SeedPouch.java b/src/com/watabou/pixeldungeon/items/bags/SeedPouch.java new file mode 100644 index 0000000000..b3515d3f5a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/bags/SeedPouch.java @@ -0,0 +1,48 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.bags; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.plants.Plant; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class SeedPouch extends Bag { + + { + name = "seed pouch"; + image = ItemSpriteSheet.POUCH; + + size = 8; + } + + @Override + public boolean grab( Item item ) { + return item instanceof Plant.Seed; + } + + @Override + public int price() { + return 50; + } + + @Override + public String info() { + return + "This small velvet pouch allows you to store any number of seeds in it. Very convenient."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/bags/WandHolster.java b/src/com/watabou/pixeldungeon/items/bags/WandHolster.java new file mode 100644 index 0000000000..eec81b4014 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/bags/WandHolster.java @@ -0,0 +1,73 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.bags; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class WandHolster extends Bag { + + { + name = "wand holster"; + image = ItemSpriteSheet.HOLSTER; + + size = 12; + } + + @Override + public boolean grab( Item item ) { + return item instanceof Wand; + } + + @Override + public boolean collect( Bag container ) { + if (super.collect( container )) { + if (owner != null) { + for (Item item : items) { + ((Wand)item).charge( owner ); + } + } + return true; + } else { + return false; + } + } + + @Override + public Item detach( Bag container ) { + + for (Item item : items) { + ((Wand)item).stopCharging(); + } + + return super.detach( container ); + } + + @Override + public int price() { + return 50; + } + + @Override + public String info() { + return + "This slim holder is made of leather of some exotic animal. " + + "It allows to compactly carry up to " + size + " wands."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/food/ChargrilledMeat.java b/src/com/watabou/pixeldungeon/items/food/ChargrilledMeat.java new file mode 100644 index 0000000000..67ffbdea87 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/food/ChargrilledMeat.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.food; + +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class ChargrilledMeat extends Food { + + { + name = "chargrilled meat"; + image = ItemSpriteSheet.STEAK; + energy = Hunger.STARVING - Hunger.HUNGRY; + } + + @Override + public String info() { + return "It looks like a decent steak."; + } + + @Override + public int price() { + return 5 * quantity; + } + + public static Food cook( MysteryMeat ingredient ) { + ChargrilledMeat result = new ChargrilledMeat(); + result.quantity = ingredient.quantity(); + return result; + } +} diff --git a/src/com/watabou/pixeldungeon/items/food/Food.java b/src/com/watabou/pixeldungeon/items/food/Food.java new file mode 100644 index 0000000000..00e7fbf177 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/food/Food.java @@ -0,0 +1,120 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.food; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.SpellSprite; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfRecharging; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; + +public class Food extends Item { + + private static final float TIME_TO_EAT = 3f; + + public static final String AC_EAT = "EAT"; + + public float energy = Hunger.HUNGRY; + public String message = "That food tasted delicious!"; + + { + stackable = true; + name = "ration of food"; + image = ItemSpriteSheet.RATION; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_EAT ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_EAT )) { + + detach( hero.belongings.backpack ); + + ((Hunger)hero.buff( Hunger.class )).satisfy( energy ); + GLog.i( message ); + + switch (hero.heroClass) { + case WARRIOR: + if (hero.HP < hero.HT) { + hero.HP = Math.min( hero.HP + 5, hero.HT ); + hero.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + } + break; + case MAGE: + hero.belongings.charge( false ); + ScrollOfRecharging.charge( hero ); + break; + case ROGUE: + case HUNTRESS: + break; + } + + hero.sprite.operate( hero.pos ); + hero.busy(); + SpellSprite.show( hero, SpellSprite.FOOD ); + Sample.INSTANCE.play( Assets.SND_EAT ); + + hero.spend( TIME_TO_EAT ); + + Statistics.foodEaten++; + Badges.validateFoodEaten(); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public String info() { + return + "Nothing fancy here: dried meat, " + + "some biscuits - things like that."; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public int price() { + return 10 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/food/FrozenCarpaccio.java b/src/com/watabou/pixeldungeon/items/food/FrozenCarpaccio.java new file mode 100644 index 0000000000..a280076025 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/food/FrozenCarpaccio.java @@ -0,0 +1,92 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.food; + +import com.watabou.pixeldungeon.actors.buffs.Barkskin; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Weakness; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Random; + +public class FrozenCarpaccio extends Food { + + { + name = "frozen carpaccio"; + image = ItemSpriteSheet.CARPACCIO; + energy = Hunger.STARVING - Hunger.HUNGRY; + } + + @Override + public void execute( Hero hero, String action ) { + + super.execute( hero, action ); + + if (action.equals( AC_EAT )) { + + switch (Random.Int( 5 )) { + case 0: + GLog.i( "You see your hands turn invisible!" ); + Buff.affect( hero, Invisibility.class, Invisibility.DURATION ); + break; + case 1: + GLog.i( "You feel your skin hardens!" ); + Buff.affect( hero, Barkskin.class ).level( hero.HT / 4 ); + break; + case 2: + GLog.i( "Refreshing!" ); + Buff.detach( hero, Poison.class ); + Buff.detach( hero, Cripple.class ); + Buff.detach( hero, Weakness.class ); + Buff.detach( hero, Bleeding.class ); + break; + case 3: + GLog.i( "You feel better!" ); + if (hero.HP < hero.HT) { + hero.HP = Math.min( hero.HP + hero.HT / 4, hero.HT ); + hero.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + } + break; + } + } + } + + @Override + public String info() { + return + "It's a piece of frozen raw meat. The only way to eat it is " + + "by cutting thin slices of it. And this way it's suprisingly good."; + } + + public int price() { + return 10 * quantity; + }; + + public static Food cook( MysteryMeat ingredient ) { + FrozenCarpaccio result = new FrozenCarpaccio(); + result.quantity = ingredient.quantity(); + return result; + } +} diff --git a/src/com/watabou/pixeldungeon/items/food/MysteryMeat.java b/src/com/watabou/pixeldungeon/items/food/MysteryMeat.java new file mode 100644 index 0000000000..56ebf3d8b2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/food/MysteryMeat.java @@ -0,0 +1,77 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.food; + +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Roots; +import com.watabou.pixeldungeon.actors.buffs.Slow; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Random; + +public class MysteryMeat extends Food { + + { + name = "mystery meat"; + image = ItemSpriteSheet.MEAT; + energy = Hunger.STARVING - Hunger.HUNGRY; + message = "That food tasted... strange."; + } + + @Override + public void execute( Hero hero, String action ) { + + super.execute( hero, action ); + + if (action.equals( AC_EAT )) { + + switch (Random.Int( 5 )) { + case 0: + GLog.w( "Oh it's hot!" ); + Buff.affect( hero, Burning.class ).reignite( hero ); + break; + case 1: + GLog.w( "You can't feel your legs!" ); + Buff.prolong( hero, Roots.class, Paralysis.duration( hero ) ); + break; + case 2: + GLog.w( "You are not feeling well." ); + Buff.affect( hero, Poison.class ).set( Poison.durationFactor( hero ) * hero.HT / 2 / Poison.DOT ); + break; + case 3: + GLog.w( "You are stuffed." ); + Buff.prolong( hero, Slow.class, Slow.duration( hero ) ); + break; + } + } + } + + @Override + public String info() { + return "Eat at your own risk!"; + } + + public int price() { + return 5 * quantity; + }; +} diff --git a/src/com/watabou/pixeldungeon/items/food/OverpricedRation.java b/src/com/watabou/pixeldungeon/items/food/OverpricedRation.java new file mode 100644 index 0000000000..4e33d64cef --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/food/OverpricedRation.java @@ -0,0 +1,41 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.food; + +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class OverpricedRation extends Food { + + { + name = "overpriced food ration"; + image = ItemSpriteSheet.OVERPRICED; + energy = Hunger.STARVING - Hunger.HUNGRY; + message = "That food tasted ok."; + } + + @Override + public String info() { + return "It looks exactly like a standard ration of food but smaller."; + } + + @Override + public int price() { + return 20 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/food/Pasty.java b/src/com/watabou/pixeldungeon/items/food/Pasty.java new file mode 100644 index 0000000000..af0706d841 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/food/Pasty.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.food; + +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Pasty extends Food { + + { + name = "pasty"; + image = ItemSpriteSheet.PASTY; + energy = Hunger.STARVING; + } + + @Override + public String info() { + return "This is authentic Cornish pasty with traditional filling of beef and potato."; + } + + @Override + public int price() { + return 20 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/keys/GoldenKey.java b/src/com/watabou/pixeldungeon/items/keys/GoldenKey.java new file mode 100644 index 0000000000..3a8f4fa458 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/keys/GoldenKey.java @@ -0,0 +1,44 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.keys; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class GoldenKey extends Key { + + { + name = "golden key"; + image = ItemSpriteSheet.GOLDEN_KEY; + } + + public GoldenKey() { + this( 0 ); + } + + public GoldenKey( int depth ) { + super(); + this.depth = depth; + } + + @Override + public String info() { + return + "The notches on this golden key are tiny and intricate. " + + "Maybe it can open some chest lock?"; + } +} diff --git a/src/com/watabou/pixeldungeon/items/keys/IronKey.java b/src/com/watabou/pixeldungeon/items/keys/IronKey.java new file mode 100644 index 0000000000..ccf33147f2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/keys/IronKey.java @@ -0,0 +1,75 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.keys; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.Utils; + +public class IronKey extends Key { + + private static final String TXT_FROM_DEPTH = "iron key from depth %d"; + + public static int curDepthQunatity = 0; + + { + name = "iron key"; + image = ItemSpriteSheet.IRON_KEY; + } + + public IronKey() { + this( 0 ); + } + + public IronKey( int depth ) { + super(); + this.depth = depth; + } + + @Override + public boolean collect( Bag bag ) { + boolean result = super.collect( bag ); + if (result && depth == Dungeon.depth && Dungeon.hero != null) { + Dungeon.hero.belongings.countIronKeys(); + } + return result; + } + + @Override + public Item detach( Bag bag ) { + Item result = super.detach( bag ); + if (result != null && depth == Dungeon.depth) { + Dungeon.hero.belongings.countIronKeys(); + } + return result; + } + + @Override + public String toString() { + return Utils.format( TXT_FROM_DEPTH, depth ); + } + + @Override + public String info() { + return + "The notches on this ancient iron key are well worn; its leather lanyard " + + "is battered by age. What door might it open?"; + } +} diff --git a/src/com/watabou/pixeldungeon/items/keys/Key.java b/src/com/watabou/pixeldungeon/items/keys/Key.java new file mode 100644 index 0000000000..c1fba7c0ae --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/keys/Key.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.keys; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.utils.Bundle; + +public class Key extends Item { + + public static final float TIME_TO_UNLOCK = 1f; + + { + stackable = true; + } + + public int depth; + + @Override + public boolean isSimilar( Item item ) { + return item.getClass() == getClass() && ((Key)item).depth == depth; + } + + @Override + public Item detach( Bag container ) { + Key key = (Key)super.detach( container ); + key.depth = depth; + return key; + } + + private static final String DEPTH = "depth"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( DEPTH, depth ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + depth = bundle.getInt( DEPTH ); + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } +} diff --git a/src/com/watabou/pixeldungeon/items/keys/SkeletonKey.java b/src/com/watabou/pixeldungeon/items/keys/SkeletonKey.java new file mode 100644 index 0000000000..95131e4836 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/keys/SkeletonKey.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.keys; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class SkeletonKey extends Key { + + { + name = "skeleton key"; + image = ItemSpriteSheet.SKELETON_KEY; + stackable = false; + } + + public SkeletonKey() { + this( 0 ); + } + + public SkeletonKey( int depth ) { + super(); + this.depth = depth; + } + + @Override + public boolean isSimilar( Item item ) { + return false; + } + + @Override + public String info() { + return + "This key looks serious: its head is shaped like a skull. Probably it can open some serious door."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/Potion.java b/src/com/watabou/pixeldungeon/items/potions/Potion.java new file mode 100644 index 0000000000..07ed50a741 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/Potion.java @@ -0,0 +1,287 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import java.util.ArrayList; +import java.util.HashSet; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Splash; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.ItemStatusHandler; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndOptions; +import com.watabou.utils.Bundle; + +public class Potion extends Item { + + public static final String AC_DRINK = "DRINK"; + + private static final String TXT_HARMFUL = "Harmful potion!"; + private static final String TXT_BENEFICIAL = "Beneficial potion"; + private static final String TXT_YES = "Yes, I know what I'm doing"; + private static final String TXT_NO = "No, I changed my mind"; + private static final String TXT_R_U_SURE_DRINK = + "Are you sure you want to drink it? In most cases you should throw such potions at your enemies."; + private static final String TXT_R_U_SURE_THROW = + "Are you sure you want to throw it? In most cases it makes sense to drink it."; + + private static final float TIME_TO_DRINK = 1f; + + private static final Class[] potions = { + PotionOfHealing.class, + PotionOfExperience.class, + PotionOfToxicGas.class, + PotionOfLiquidFlame.class, + PotionOfStrength.class, + PotionOfParalyticGas.class, + PotionOfLevitation.class, + PotionOfMindVision.class, + PotionOfPurity.class, + PotionOfInvisibility.class, + PotionOfMight.class, + PotionOfFrost.class + }; + private static final String[] colors = { + "turquoise", "crimson", "azure", "jade", "golden", "magenta", + "charcoal", "ivory", "amber", "bistre", "indigo", "silver"}; + private static final Integer[] images = { + ItemSpriteSheet.POTION_TURQUOISE, + ItemSpriteSheet.POTION_CRIMSON, + ItemSpriteSheet.POTION_AZURE, + ItemSpriteSheet.POTION_JADE, + ItemSpriteSheet.POTION_GOLDEN, + ItemSpriteSheet.POTION_MAGENTA, + ItemSpriteSheet.POTION_CHARCOAL, + ItemSpriteSheet.POTION_IVORY, + ItemSpriteSheet.POTION_AMBER, + ItemSpriteSheet.POTION_BISTRE, + ItemSpriteSheet.POTION_INDIGO, + ItemSpriteSheet.POTION_SILVER}; + + private static ItemStatusHandler handler; + + private String color; + + { + stackable = true; + defaultAction = AC_DRINK; + } + + @SuppressWarnings("unchecked") + public static void initColors() { + handler = new ItemStatusHandler( (Class[])potions, colors, images ); + } + + public static void save( Bundle bundle ) { + handler.save( bundle ); + } + + @SuppressWarnings("unchecked") + public static void restore( Bundle bundle ) { + handler = new ItemStatusHandler( (Class[])potions, colors, images, bundle ); + } + + public Potion() { + super(); + image = handler.image( this ); + color = handler.label( this ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_DRINK ); + return actions; + } + + @Override + public void execute( final Hero hero, String action ) { + if (action.equals( AC_DRINK )) { + + if (isKnown() && ( + this instanceof PotionOfLiquidFlame || + this instanceof PotionOfToxicGas || + this instanceof PotionOfParalyticGas)) { + + GameScene.show( + new WndOptions( TXT_HARMFUL, TXT_R_U_SURE_DRINK, TXT_YES, TXT_NO ) { + @Override + protected void onSelect(int index) { + if (index == 0) { + drink( hero ); + } + }; + } + ); + + } else { + drink( hero ); + } + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public void doThrow( final Hero hero ) { + + if (isKnown() && ( + this instanceof PotionOfExperience || + this instanceof PotionOfHealing || + this instanceof PotionOfLevitation || + this instanceof PotionOfMindVision || + this instanceof PotionOfStrength || + this instanceof PotionOfInvisibility || + this instanceof PotionOfMight)) { + + GameScene.show( + new WndOptions( TXT_BENEFICIAL, TXT_R_U_SURE_THROW, TXT_YES, TXT_NO ) { + @Override + protected void onSelect(int index) { + if (index == 0) { + Potion.super.doThrow( hero ); + } + }; + } + ); + + } else { + super.doThrow( hero ); + } + } + + protected void drink( Hero hero ) { + + detach( hero.belongings.backpack ); + + hero.spend( TIME_TO_DRINK ); + hero.busy(); + onThrow( hero.pos ); + + Sample.INSTANCE.play( Assets.SND_DRINK ); + + hero.sprite.operate( hero.pos ); + } + + @Override + protected void onThrow( int cell ) { + if (Dungeon.hero.pos == cell) { + + apply( Dungeon.hero ); + + } else if (Dungeon.level.map[cell] == Terrain.WELL || Level.pit[cell]) { + + super.onThrow( cell ); + + } else { + + shatter( cell ); + + } + } + + protected void apply( Hero hero ) { + shatter( hero.pos ); + } + + protected void shatter( int cell ) { + GLog.i( "The flask shatters and " + color() + " liquid splashes harmlessly" ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + splash( cell ); + } + + public boolean isKnown() { + return handler.isKnown( this ); + } + + public void setKnown() { + if (!isKnown()) { + handler.know( this ); + } + + Badges.validateAllPotionsIdentified(); + } + + @Override + public Item identify() { + setKnown(); + return this; + } + + protected String color() { + return color; + } + + @Override + public String name() { + return isKnown() ? name : color + " potion"; + } + + @Override + public String info() { + return isKnown() ? + desc() : + "This flask contains a swirling " + color + " liquid. " + + "Who knows what it will do when drunk or thrown?"; + } + + @Override + public boolean isIdentified() { + return isKnown(); + } + + @Override + public boolean isUpgradable() { + return false; + } + + public static HashSet> getKnown() { + return handler.known(); + } + + public static HashSet> getUnknown() { + return handler.unknown(); + } + + public static boolean allKnown() { + return handler.known().size() == potions.length; + } + + protected void splash( int cell ) { + final int color = ItemSprite.pick( image, 8, 10 ); + Splash.at( cell, color, 5 ); + } + + @Override + public int price() { + return 20 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfExperience.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfExperience.java new file mode 100644 index 0000000000..2288619331 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfExperience.java @@ -0,0 +1,45 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.pixeldungeon.actors.hero.Hero; + +public class PotionOfExperience extends Potion { + + { + name = "Potion of Experience"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + hero.earnExp( hero.maxExp() - hero.exp ); + } + + @Override + public String desc() { + return + "The storied experiences of multitudes of battles reduced to liquid form, " + + "this draught will instantly raise your experience level."; + } + + @Override + public int price() { + return isKnown() ? 80 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfFrost.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfFrost.java new file mode 100644 index 0000000000..408b918221 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfFrost.java @@ -0,0 +1,67 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.actors.blobs.Freezing; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.utils.PathFinder; + +public class PotionOfFrost extends Potion { + + private static final int DISTANCE = 2; + + { + name = "Potion of Frost"; + } + + @Override + protected void shatter( int cell ) { + + PathFinder.buildDistanceMap( cell, BArray.not( Level.losBlocking, null ), DISTANCE ); + + Fire fire = (Fire)Dungeon.level.blobs.get( Fire.class ); + + for (int i=0; i < Level.LENGTH; i++) { + if (PathFinder.distance[i] < Integer.MAX_VALUE) { + Freezing.affect( i, fire ); + } + } + + splash( cell ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + + setKnown(); + } + + @Override + public String desc() { + return + "Upon exposure to open air this chemical will evaporate into a freezing cloud, causing " + + "any creature that contacts it to be frozen in place unable to act and move."; + } + + @Override + public int price() { + return isKnown() ? 50 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfHealing.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfHealing.java new file mode 100644 index 0000000000..aa2e46a782 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfHealing.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.buffs.Weakness; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.utils.GLog; + +public class PotionOfHealing extends Potion { + + { + name = "Potion of Healing"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + heal( Dungeon.hero ); + GLog.p( "Your wounds heal completely." ); + } + + public static void heal( Hero hero ) { + + hero.HP = hero.HT; + Buff.detach( hero, Poison.class ); + Buff.detach( hero, Cripple.class ); + Buff.detach( hero, Weakness.class ); + Buff.detach( hero, Bleeding.class ); + + hero.sprite.emitter().start( Speck.factory( Speck.HEALING ), 0.4f, 4 ); + } + + @Override + public String desc() { + return + "An elixir that will instantly return you to full health and cure poison."; + } + + @Override + public int price() { + return isKnown() ? 30 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfInvisibility.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfInvisibility.java new file mode 100644 index 0000000000..5cd804f18b --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfInvisibility.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.utils.GLog; + +public class PotionOfInvisibility extends Potion { + + private static final float ALPHA = 0.4f; + + { + name = "Potion of Invisibility"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + Buff.affect( hero, Invisibility.class, Invisibility.DURATION ); + GLog.i( "You see your hands turn invisible!" ); + Sample.INSTANCE.play( Assets.SND_MELD ); + } + + @Override + public String desc() { + return + "Drinking this potion will render you temporarily invisible. While invisible, " + + "enemies will be unable to see you. Attacking an enemy, as well as using a wand or a scroll " + + "before enemy's eyes, will dispel the effect."; + } + + @Override + public int price() { + return isKnown() ? 40 * quantity : super.price(); + } + + public static void melt( Char ch ) { + if (ch.sprite.parent != null) { + ch.sprite.parent.add( new AlphaTweener( ch.sprite, ALPHA, 0.4f ) ); + } else { + ch.sprite.alpha( ALPHA ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfLevitation.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfLevitation.java new file mode 100644 index 0000000000..cc5e9d7bb4 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfLevitation.java @@ -0,0 +1,50 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Levitation; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.utils.GLog; + +public class PotionOfLevitation extends Potion { + + { + name = "Potion of Levitation"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + Buff.affect( hero, Levitation.class, Levitation.DURATION ); + GLog.i( "You float into the air!" ); + } + + @Override + public String desc() { + return + "Drinking this curious liquid will cause you to hover in the air, " + + "able to drift effortlessly over traps. Flames and gases " + + "fill the air, however, and cannot be bypassed while airborne."; + } + + @Override + public int price() { + return isKnown() ? 35 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfLiquidFlame.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfLiquidFlame.java new file mode 100644 index 0000000000..f728124592 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfLiquidFlame.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class PotionOfLiquidFlame extends Potion { + + { + name = "Potion of Liquid Flame"; + } + + @Override + protected void shatter( int cell ) { + + setKnown(); + + splash( cell ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + + Fire fire = Blob.seed( cell, 2, Fire.class ); + GameScene.add( fire ); + } + + @Override + public String desc() { + return + "This flask contains an unstable compound which will burst " + + "violently into flame upon exposure to open air."; + } + + @Override + public int price() { + return isKnown() ? 40 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfMight.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfMight.java new file mode 100644 index 0000000000..d560a550e1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfMight.java @@ -0,0 +1,52 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.utils.GLog; + +public class PotionOfMight extends PotionOfStrength { + + { + name = "Potion of Might"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + + hero.STR++; + hero.HT += 5; + hero.HP += 5; + hero.sprite.showStatus( CharSprite.POSITIVE, "+1 str, +5 ht" ); + GLog.p( "Newfound strength surges through your body." ); + } + + @Override + public String desc() { + return + "This powerful liquid will course through your muscles, permanently " + + "increasing your strength by one point and health by five points."; + } + + @Override + public int price() { + return isKnown() ? 200 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfMindVision.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfMindVision.java new file mode 100644 index 0000000000..7b155bb368 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfMindVision.java @@ -0,0 +1,57 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.MindVision; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.utils.GLog; + +public class PotionOfMindVision extends Potion { + + { + name = "Potion of Mind Vision"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + Buff.affect( hero, MindVision.class, MindVision.DURATION ); + Dungeon.observe(); + + if (Dungeon.level.mobs.size() > 0) { + GLog.i( "You can somehow feel the presence of other creatures' minds!" ); + } else { + GLog.i( "You can somehow tell that you are alone on this level at the moment." ); + } + } + + @Override + public String desc() { + return + "After drinking this, your mind will become attuned to the psychic signature " + + "of distant creatures, enabling you to sense biological presences through walls. " + + "Also this potion will permit you to see through nearby walls and doors."; + } + + @Override + public int price() { + return isKnown() ? 35 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfParalyticGas.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfParalyticGas.java new file mode 100644 index 0000000000..7678bd2c7e --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfParalyticGas.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ParalyticGas; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class PotionOfParalyticGas extends Potion { + + { + name = "Potion of Paralytic Gas"; + } + + @Override + protected void shatter( int cell ) { + + setKnown(); + + splash( cell ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + + GameScene.add( Blob.seed( cell, 1000, ParalyticGas.class ) ); + } + + @Override + public String desc() { + return + "Upon exposure to open air, the liquid in this flask will vaporize " + + "into a numbing yellow haze. Anyone who inhales the cloud will be paralyzed " + + "instantly, unable to move for some time after the cloud dissipates. This " + + "item can be thrown at distant enemies to catch them within the effect of the gas."; + } + + @Override + public int price() { + return isKnown() ? 40 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfPurity.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfPurity.java new file mode 100644 index 0000000000..d0e04ff52f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfPurity.java @@ -0,0 +1,126 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ParalyticGas; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.GasesImmunity; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.PathFinder; + +public class PotionOfPurity extends Potion { + + private static final String TXT_FRESHNESS = "You feel uncommon freshness in the air."; + private static final String TXT_NO_SMELL = "You've stopped sensing any smells!"; + + private static final int DISTANCE = 2; + + { + name = "Potion of Purification"; + } + + @Override + protected void shatter( int cell ) { + + PathFinder.buildDistanceMap( cell, BArray.not( Level.losBlocking, null ), DISTANCE ); + + boolean procd = false; + + Blob[] blobs = { + Dungeon.level.blobs.get( ToxicGas.class ), + Dungeon.level.blobs.get( ParalyticGas.class ) + }; + + for (int j=0; j < blobs.length; j++) { + + Blob blob = blobs[j]; + if (blob == null) { + continue; + } + + for (int i=0; i < Level.LENGTH; i++) { + if (PathFinder.distance[i] < Integer.MAX_VALUE) { + + int value = blob.cur[i]; + if (value > 0) { + + blob.cur[i] = 0; + blob.volume -= value; + procd = true; + + CellEmitter.get( i ).burst( Speck.factory( Speck.DISCOVER ), 1 ); + } + + } + } + } + + boolean heroAffected = PathFinder.distance[Dungeon.hero.pos] < Integer.MAX_VALUE; + + if (procd) { + + splash( cell ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + + setKnown(); + + if (heroAffected) { + GLog.p( TXT_FRESHNESS ); + } + + } else { + + super.shatter( cell ); + + if (heroAffected) { + GLog.i( TXT_FRESHNESS ); + setKnown(); + } + + } + } + + @Override + protected void apply( Hero hero ) { + GLog.w( TXT_NO_SMELL ); + Buff.prolong( hero, GasesImmunity.class, GasesImmunity.DURATION ); + setKnown(); + } + + @Override + public String desc() { + return + "This reagent will quickly neutralize all harmful gases in the area of effect. " + + "Drinking it will give you a temporary immunity to such gases."; + } + + @Override + public int price() { + return isKnown() ? 50 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfStrength.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfStrength.java new file mode 100644 index 0000000000..1e0b072b01 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfStrength.java @@ -0,0 +1,53 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.utils.GLog; + +public class PotionOfStrength extends Potion { + + { + name = "Potion of Strength"; + } + + @Override + protected void apply( Hero hero ) { + setKnown(); + + hero.STR++; + hero.sprite.showStatus( CharSprite.POSITIVE, "+1 str" ); + GLog.p( "Newfound strength surges through your body." ); + + Badges.validateStrengthAttained(); + } + + @Override + public String desc() { + return + "This powerful liquid will course through your muscles, " + + "permanently increasing your strength by one point."; + } + + @Override + public int price() { + return isKnown() ? 100 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/potions/PotionOfToxicGas.java b/src/com/watabou/pixeldungeon/items/potions/PotionOfToxicGas.java new file mode 100644 index 0000000000..be8a32cab7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/potions/PotionOfToxicGas.java @@ -0,0 +1,59 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.potions; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class PotionOfToxicGas extends Potion { + + { + name = "Potion of Toxic Gas"; + } + + @Override + protected void shatter( int cell ) { + + setKnown(); + + splash( cell ); + Sample.INSTANCE.play( Assets.SND_SHATTER ); + + ToxicGas gas = Blob.seed( cell, 1000, ToxicGas.class ); + Actor.add( gas ); + GameScene.add( gas ); + } + + @Override + public String desc() { + return + "Uncorking or shattering this pressurized glass will cause " + + "its contents to explode into a deadly cloud of toxic green gas. " + + "You might choose to fling this potion at distant enemies " + + "instead of uncorking it by hand."; + } + + @Override + public int price() { + return isKnown() ? 40 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/quest/CorpseDust.java b/src/com/watabou/pixeldungeon/items/quest/CorpseDust.java new file mode 100644 index 0000000000..be30f17477 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/quest/CorpseDust.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.quest; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class CorpseDust extends Item { + + { + name = "corpse dust"; + image = ItemSpriteSheet.DUST; + + cursed = true; + cursedKnown = true; + + unique = true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "The ball of corpse dust doesn't differ outwardly from a regular dust ball. However, " + + "you know somehow that it's better to get rid of it as soon as possible."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/quest/DarkGold.java b/src/com/watabou/pixeldungeon/items/quest/DarkGold.java new file mode 100644 index 0000000000..ef94c85a97 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/quest/DarkGold.java @@ -0,0 +1,54 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.quest; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class DarkGold extends Item { + + { + name = "dark gold ore"; + image = ItemSpriteSheet.ORE; + + stackable = true; + unique = true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "This metal is called dark not because of its color (it doesn't differ from the normal gold), " + + "but because it melts under the daylight, making it useless on the surface."; + } + + @Override + public int price() { + return quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/quest/DriedRose.java b/src/com/watabou/pixeldungeon/items/quest/DriedRose.java new file mode 100644 index 0000000000..be751a5c2d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/quest/DriedRose.java @@ -0,0 +1,47 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.quest; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class DriedRose extends Item { + + { + name = "dried rose"; + image = ItemSpriteSheet.ROSE; + + unique = true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "The rose has dried long ago, but it has kept all its petals somehow."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/quest/DwarfToken.java b/src/com/watabou/pixeldungeon/items/quest/DwarfToken.java new file mode 100644 index 0000000000..3ab8873472 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/quest/DwarfToken.java @@ -0,0 +1,54 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.quest; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class DwarfToken extends Item { + + { + name = "dwarf token"; + image = ItemSpriteSheet.TOKEN; + + stackable = true; + unique = true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "Many dwarves and some of their larger creations carry these small pieces of metal of unknown purpose. " + + "Maybe they are jewelry or maybe some kind of ID. Dwarves are strange folk."; + } + + @Override + public int price() { + return quantity * 100; + } +} diff --git a/src/com/watabou/pixeldungeon/items/quest/Pickaxe.java b/src/com/watabou/pixeldungeon/items/quest/Pickaxe.java new file mode 100644 index 0000000000..03caf593fa --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/quest/Pickaxe.java @@ -0,0 +1,177 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.quest; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Hunger; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Bat; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; +import com.watabou.utils.Callback; + +public class Pickaxe extends Weapon { + + public static final String AC_MINE = "MINE"; + + public static final float TIME_TO_MINE = 2; + + private static final String TXT_NO_VEIN = "There is no dark gold vein near you to mine"; + + private static final Glowing BLOODY = new Glowing( 0x550000 ); + + { + name = "pickaxe"; + image = ItemSpriteSheet.PICKAXE; + + unique = true; + + defaultAction = AC_MINE; + + STR = 14; + MIN = 3; + MAX = 12; + } + + public boolean bloodStained = false; + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_MINE ); + return actions; + } + + @Override + public void execute( final Hero hero, String action ) { + + if (action == AC_MINE) { + + if (Dungeon.depth < 11 || Dungeon.depth > 15) { + GLog.w( TXT_NO_VEIN ); + return; + } + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + + final int pos = hero.pos + Level.NEIGHBOURS8[i]; + if (Dungeon.level.map[pos] == Terrain.WALL_DECO) { + + hero.spend( TIME_TO_MINE ); + hero.busy(); + + hero.sprite.attack( pos, new Callback() { + + @Override + public void call() { + + CellEmitter.center( pos ).burst( Speck.factory( Speck.STAR ), 7 ); + Sample.INSTANCE.play( Assets.SND_EVOKE ); + + Level.set( pos, Terrain.WALL ); + GameScene.updateMap( pos ); + + DarkGold gold = new DarkGold(); + if (gold.doPickUp( Dungeon.hero )) { + GLog.i( Hero.TXT_YOU_NOW_HAVE, gold.name() ); + } else { + Dungeon.level.drop( gold, hero.pos ).sprite.drop(); + } + + Hunger hunger = hero.buff( Hunger.class ); + if (hunger != null && !hunger.isStarving()) { + hunger.satisfy( -Hunger.STARVING / 10 ); + BuffIndicator.refreshHero(); + } + + hero.onOperateComplete(); + } + } ); + + return; + } + } + + GLog.w( TXT_NO_VEIN ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + if (!bloodStained && defender instanceof Bat && (defender.HP <= damage)) { + bloodStained = true; + updateQuickslot(); + } + } + + private static final String BLOODSTAINED = "bloodStained"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + + bundle.put( BLOODSTAINED, bloodStained ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + + bloodStained = bundle.getBoolean( BLOODSTAINED ); + } + + @Override + public Glowing glowing() { + return bloodStained ? BLOODY : null; + } + + @Override + public String info() { + return + "This is a large and sturdy tool for breaking rocks. Probably it can be used as a weapon."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/quest/RatSkull.java b/src/com/watabou/pixeldungeon/items/quest/RatSkull.java new file mode 100644 index 0000000000..612e0af7fa --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/quest/RatSkull.java @@ -0,0 +1,52 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.quest; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class RatSkull extends Item { + + { + name = "giant rat skull"; + image = ItemSpriteSheet.SKULL; + + unique = true; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + return + "It could be a nice hunting trophy, but it smells too bad to place it on a wall."; + } + + @Override + public int price() { + return 100; + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/Ring.java b/src/com/watabou/pixeldungeon/items/rings/Ring.java new file mode 100644 index 0000000000..3f54863aff --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/Ring.java @@ -0,0 +1,324 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.items.EquipableItem; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.ItemStatusHandler; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Ring extends EquipableItem { + + private static final float TIME_TO_EQUIP = 1f; + + private static final String TXT_IDENTIFY = + "you are now familiar enough with your %s to identify it. It is %s."; + + protected Buff buff; + + private static final Class[] rings = { + RingOfMending.class, + RingOfDetection.class, + RingOfShadows.class, + RingOfPower.class, + RingOfHerbalism.class, + RingOfAccuracy.class, + RingOfEvasion.class, + RingOfSatiety.class, + RingOfHaste.class, + RingOfHaggler.class, + RingOfElements.class, + RingOfThorns.class + }; + private static final String[] gems = + {"diamond", "opal", "garnet", "ruby", "amethyst", "topaz", "onyx", "tourmaline", "emerald", "sapphire", "quartz", "agate"}; + private static final Integer[] images = { + ItemSpriteSheet.RING_DIAMOND, + ItemSpriteSheet.RING_OPAL, + ItemSpriteSheet.RING_GARNET, + ItemSpriteSheet.RING_RUBY, + ItemSpriteSheet.RING_AMETHYST, + ItemSpriteSheet.RING_TOPAZ, + ItemSpriteSheet.RING_ONYX, + ItemSpriteSheet.RING_TOURMALINE, + ItemSpriteSheet.RING_EMERALD, + ItemSpriteSheet.RING_SAPPHIRE, + ItemSpriteSheet.RING_QUARTZ, + ItemSpriteSheet.RING_AGATE}; + + private static ItemStatusHandler handler; + + private String gem; + + private int ticksToKnow = 200; + + @SuppressWarnings("unchecked") + public static void initGems() { + handler = new ItemStatusHandler( (Class[])rings, gems, images ); + } + + public static void save( Bundle bundle ) { + handler.save( bundle ); + } + + @SuppressWarnings("unchecked") + public static void restore( Bundle bundle ) { + handler = new ItemStatusHandler( (Class[])rings, gems, images, bundle ); + } + + public Ring() { + super(); + syncGem(); + } + + public void syncGem() { + image = handler.image( this ); + gem = handler.label( this ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( isEquipped( hero ) ? AC_UNEQUIP : AC_EQUIP ); + return actions; + } + + @Override + public boolean doEquip( Hero hero ) { + + if (hero.belongings.ring1 != null && hero.belongings.ring2 != null) { + + GLog.w( "you can only wear 2 rings at a time" ); + return false; + + } else { + + if (hero.belongings.ring1 == null) { + hero.belongings.ring1 = this; + } else { + hero.belongings.ring2 = this; + } + + detach( hero.belongings.backpack ); + + activate( hero ); + + cursedKnown = true; + if (cursed) { + equipCursed( hero ); + GLog.n( "your " + this + " tightens around your finger painfully" ); + } + + hero.spendAndNext( TIME_TO_EQUIP ); + return true; + + } + + } + + public void activate( Char ch ) { + buff = buff(); + buff.attachTo( ch ); + } + + @Override + public boolean doUnequip( Hero hero, boolean collect ) { + + if (cursed) { + GLog.w( "You can't remove cursed " + name() + "!" ); + return false; + } + + if (hero.belongings.ring1 == this) { + hero.belongings.ring1 = null; + } else { + hero.belongings.ring2 = null; + } + + hero.remove( buff ); + buff = null; + + hero.spendAndNext( TIME_TO_EQUIP ); + + if (collect && !collect( hero.belongings.backpack )) { + Dungeon.level.drop( this, hero.pos ); + } + + return true; + } + + @Override + public boolean isEquipped( Hero hero ) { + return hero.belongings.ring1 == this || hero.belongings.ring2 == this; + } + + @Override + public Item upgrade() { + + super.upgrade(); + + if (buff != null) { + + Char owner = buff.target; + buff.detach(); + if ((buff = buff()) != null) { + buff.attachTo( owner ); + } + } + + return this; + } + + public boolean isKnown() { + return handler.isKnown( this ); + } + + protected void setKnown() { + if (!isKnown()) { + handler.know( this ); + } + + Badges.validateAllRingsIdentified(); + } + + @Override + public String name() { + return isKnown() ? name : gem + " ring"; + } + + @Override + public String desc() { + return + "This metal band is adorned with a large " + gem + " gem " + + "that glitters in the darkness. Who knows what effect it has when worn?"; + } + + @Override + public String info() { + if (isEquipped( Dungeon.hero )) { + + return desc() + "\n\n" + "The " + name() + " is on your finger" + + (cursed ? ", and because it is cursed, you are powerless to remove it." : "." ); + + } else if (cursed && cursedKnown) { + + return desc() + "\n\nYou can feel a malevolent magic lurking within the " + name() + "."; + + } else { + + return desc(); + + } + } + + @Override + public boolean isIdentified() { + return super.isIdentified() && isKnown(); + } + + @Override + public Item identify() { + setKnown(); + return super.identify(); + } + + @Override + public Item random() { + level = Random.Int( 1, 3 ); + if (Random.Float() < 0.3f) { + level = -level; + cursed = true; + } + return this; + } + + public static boolean allKnown() { + return handler.known().size() == rings.length - 2; + } + + @Override + public int price() { + int price = 80; + if (cursed && cursedKnown) { + price /= 2; + } + if (levelKnown) { + if (level > 0) { + price *= (level + 1); + } else if (level < 0) { + price /= (1 - level); + } + } + if (price < 1) { + price = 1; + } + return price; + } + + protected RingBuff buff() { + return null; + } + + public class RingBuff extends Buff { + + private static final String TXT_KNOWN = "This is a %s"; + + public int level; + public RingBuff() { + level = Ring.this.level; + } + + @Override + public boolean attachTo( Char target ) { + + if (target instanceof Hero && ((Hero)target).heroClass == HeroClass.ROGUE && !isKnown()) { + setKnown(); + GLog.i( TXT_KNOWN, name() ); + Badges.validateItemLevelAquired( Ring.this ); + } + + return super.attachTo(target); + } + + @Override + public boolean act() { + + if (!isIdentified() && --ticksToKnow <= 0) { + String gemName = name(); + identify(); + GLog.w( TXT_IDENTIFY, gemName, Ring.this.toString() ); + Badges.validateItemLevelAquired( Ring.this ); + } + + spend( TICK ); + + return true; + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfAccuracy.java b/src/com/watabou/pixeldungeon/items/rings/RingOfAccuracy.java new file mode 100644 index 0000000000..1fd4799b8f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfAccuracy.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfAccuracy extends Ring { + + { + name = "Ring of Accuracy"; + } + + @Override + protected RingBuff buff( ) { + return new Accuracy(); + } + + @Override + public String desc() { + return isKnown() ? + "This ring increases your chance to hit the enemy." : + super.desc(); + } + + public class Accuracy extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfDetection.java b/src/com/watabou/pixeldungeon/items/rings/RingOfDetection.java new file mode 100644 index 0000000000..6f54dfa02c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfDetection.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; + +public class RingOfDetection extends Ring { + + { + name = "Ring of Detection"; + } + + @Override + public boolean doEquip( Hero hero ) { + if (super.doEquip( hero )) { + Dungeon.hero.search( false ); + return true; + } else { + return false; + } + } + + @Override + protected RingBuff buff( ) { + return new Detection(); + } + + @Override + public String desc() { + return isKnown() ? + "Wearing this ring will allow the wearer to notice hidden secrets - " + + "traps and secret doors - without taking time to search. Degraded rings of detection " + + "will dull your senses, making it harder to notice secrets even when actively searching for them." : + super.desc(); + } + + public class Detection extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfElements.java b/src/com/watabou/pixeldungeon/items/rings/RingOfElements.java new file mode 100644 index 0000000000..bfc8ab1945 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfElements.java @@ -0,0 +1,77 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +import java.util.HashSet; + +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.actors.mobs.Eye; +import com.watabou.pixeldungeon.actors.mobs.Warlock; +import com.watabou.pixeldungeon.actors.mobs.Yog; +import com.watabou.pixeldungeon.levels.traps.LightningTrap; +import com.watabou.utils.Random; + +public class RingOfElements extends Ring { + + { + name = "Ring of Elements"; + } + + @Override + protected RingBuff buff( ) { + return new Resistance(); + } + + @Override + public String desc() { + return isKnown() ? + "This ring provides resistance to different elements, such as fire, " + + "electricity, gases etc. Also it decreases duration of negative effects." : + super.desc(); + } + + private static final HashSet> EMPTY = new HashSet>(); + private static final HashSet> FULL; + static { + FULL = new HashSet>(); + FULL.add( Burning.class ); + FULL.add( ToxicGas.class ); + FULL.add( Poison.class ); + FULL.add( LightningTrap.Electricity.class ); + FULL.add( Warlock.class ); + FULL.add( Eye.class ); + FULL.add( Yog.BurningFist.class ); + } + + public class Resistance extends RingBuff { + + public HashSet> resistances() { + if (Random.Int( level + 3 ) >= 3) { + return FULL; + } else { + return EMPTY; + } + } + + public float durationFactor() { + return level < 0 ? 1 : (2 + 0.5f * level) / (2 + level); + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfEvasion.java b/src/com/watabou/pixeldungeon/items/rings/RingOfEvasion.java new file mode 100644 index 0000000000..bcdadbc459 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfEvasion.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfEvasion extends Ring { + + { + name = "Ring of Evasion"; + } + + @Override + protected RingBuff buff( ) { + return new Evasion(); + } + + @Override + public String desc() { + return isKnown() ? + "This ring increases your chance to dodge enemy attack." : + super.desc(); + } + + public class Evasion extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfHaggler.java b/src/com/watabou/pixeldungeon/items/rings/RingOfHaggler.java new file mode 100644 index 0000000000..704641f31f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfHaggler.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; + +public class RingOfHaggler extends Ring { + + { + name = "Ring of Haggler"; + } + + @Override + protected RingBuff buff( ) { + return new Haggling(); + } + + @Override + public Item random() { + level = +1; + return this; + } + + @Override + public boolean doPickUp( Hero hero ) { + identify(); + Badges.validateRingOfHaggler(); + Badges.validateItemLevelAquired( this ); + return super.doPickUp(hero); + } + + @Override + public boolean isUpgradable() { + return false; + } + + + @Override + public String desc() { + return isKnown() ? + "In fact this ring doesn't provide any magic effect, but it demonstrates " + + "to shopkeepers and vendors, that the owner of the ring is a member of " + + "The Thieves' Guild. Usually they are glad to give a discount in exchange " + + "for temporary immunity guarantee. Upgrading this ring won't give any additional " + + "bonuses." : + super.desc(); + } + + public class Haggling extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfHaste.java b/src/com/watabou/pixeldungeon/items/rings/RingOfHaste.java new file mode 100644 index 0000000000..26c9afbdbf --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfHaste.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfHaste extends Ring { + + { + name = "Ring of Haste"; + } + + @Override + protected RingBuff buff( ) { + return new Haste(); + } + + @Override + public String desc() { + return isKnown() ? + "This ring accelerates the wearer's flow of time, allowing one to perform all actions a little faster." : + super.desc(); + } + + public class Haste extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfHerbalism.java b/src/com/watabou/pixeldungeon/items/rings/RingOfHerbalism.java new file mode 100644 index 0000000000..4ed07ec84d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfHerbalism.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfHerbalism extends Ring { + + { + name = "Ring of Herbalism"; + } + + @Override + protected RingBuff buff( ) { + return new Herbalism(); + } + + @Override + public String desc() { + return isKnown() ? + "This ring increases your chance to gather dew and seeds from trampled grass." : + super.desc(); + } + + public class Herbalism extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfMending.java b/src/com/watabou/pixeldungeon/items/rings/RingOfMending.java new file mode 100644 index 0000000000..f1a3490871 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfMending.java @@ -0,0 +1,42 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfMending extends Ring { + + { + name = "Ring of Mending"; + } + + @Override + protected RingBuff buff( ) { + return new Rejuvenation(); + } + + @Override + public String desc() { + return isKnown() ? + "This ring increases the body's regenerative properties, allowing " + + "one to recover lost health at an accelerated rate. Degraded rings will " + + "decrease or even halt one's natural regeneration." : + super.desc(); + } + + public class Rejuvenation extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfPower.java b/src/com/watabou/pixeldungeon/items/rings/RingOfPower.java new file mode 100644 index 0000000000..372a300d1c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfPower.java @@ -0,0 +1,41 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfPower extends Ring { + + { + name = "Ring of Power"; + } + + @Override + protected RingBuff buff( ) { + return new Power(); + } + + @Override + public String desc() { + return isKnown() ? + "Your wands will become more powerful in the energy field " + + "that radiates from this ring. Degraded rings of power will instead weaken your wands." : + super.desc(); + } + + public class Power extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfSatiety.java b/src/com/watabou/pixeldungeon/items/rings/RingOfSatiety.java new file mode 100644 index 0000000000..c2adb20f94 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfSatiety.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfSatiety extends Ring { + + { + name = "Ring of Satiety"; + } + + @Override + protected RingBuff buff( ) { + return new Satiety(); + } + + @Override + public String desc() { + return isKnown() ? + "Wearing this ring you can go without food longer. Degraded rings of satiety will cause the opposite effect." : + super.desc(); + } + + public class Satiety extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfShadows.java b/src/com/watabou/pixeldungeon/items/rings/RingOfShadows.java new file mode 100644 index 0000000000..999f829f19 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfShadows.java @@ -0,0 +1,41 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +public class RingOfShadows extends Ring { + + { + name = "Ring of Shadows"; + } + + @Override + protected RingBuff buff( ) { + return new Shadows(); + } + + @Override + public String desc() { + return isKnown() ? + "Enemies will be less likely to notice you if you wear this ring. Degraded rings " + + "of shadows will alert enemies who might otherwise not have noticed your presence." : + super.desc(); + } + + public class Shadows extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/rings/RingOfThorns.java b/src/com/watabou/pixeldungeon/items/rings/RingOfThorns.java new file mode 100644 index 0000000000..43bd431fd8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/rings/RingOfThorns.java @@ -0,0 +1,66 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.rings; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; + +public class RingOfThorns extends Ring { + + { + name = "Ring of Thorns"; + } + + @Override + protected RingBuff buff( ) { + return new Thorns(); + } + + @Override + public Item random() { + level = +1; + return this; + } + + @Override + public boolean doPickUp( Hero hero ) { + identify(); + Badges.validateRingOfThorns(); + Badges.validateItemLevelAquired( this ); + return super.doPickUp(hero); + } + + @Override + public boolean isUpgradable() { + return false; + } + + + @Override + public String desc() { + return isKnown() ? + "Though this ring doesn't provide real thorns, an enemy that attacks you " + + "will itself be wounded by a fraction of the damage that it inflicts. " + + "Upgrading this ring won't give any additional bonuses." : + super.desc(); + } + + public class Thorns extends RingBuff { + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/InventoryScroll.java b/src/com/watabou/pixeldungeon/items/scrolls/InventoryScroll.java new file mode 100644 index 0000000000..ec217e1753 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/InventoryScroll.java @@ -0,0 +1,95 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.windows.WndBag; +import com.watabou.pixeldungeon.windows.WndOptions; + +public abstract class InventoryScroll extends Scroll { + + protected String inventoryTitle = "Select an item"; + protected WndBag.Mode mode = WndBag.Mode.ALL; + + private static final String TXT_WARNING = + "Do you really want to cancel this scroll usage? " + + "It will be consumed anyway."; + private static final String TXT_YES = "Yes, I'm positive"; + private static final String TXT_NO = "No, I changed my mind"; + + @Override + protected void doRead() { + + if (!isKnown()) { + setKnown(); + identifiedByUse = true; + } else { + identifiedByUse = false; + } + + GameScene.selectItem( itemSelector, mode, inventoryTitle ); + } + + private void confirmCancelation() { + GameScene.show( new WndOptions( name(), TXT_WARNING, TXT_YES, TXT_NO ) { + @Override + protected void onSelect( int index ) { + switch (index) { + case 0: + curUser.spendAndNext( TIME_TO_READ ); + identifiedByUse = false; + break; + case 1: + GameScene.selectItem( itemSelector, mode, inventoryTitle ); + break; + } + } + public void onBackPressed() {}; + } ); + } + + protected abstract void onItemSelected( Item item ); + + protected static boolean identifiedByUse = false; + protected static WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + + ((InventoryScroll)curItem).onItemSelected( item ); + curUser.spendAndNext( TIME_TO_READ ); + + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + } else if (identifiedByUse) { + + ((InventoryScroll)curItem).confirmCancelation(); + + } else { + + curItem.collect( curUser.belongings.backpack ); + + } + } + }; +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/Scroll.java b/src/com/watabou/pixeldungeon/items/scrolls/Scroll.java new file mode 100644 index 0000000000..711dac28d2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/Scroll.java @@ -0,0 +1,184 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import java.util.ArrayList; +import java.util.HashSet; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.ItemStatusHandler; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; + +public abstract class Scroll extends Item { + + private static final String TXT_BLINDED = "You can't read a scroll while blinded"; + + public static final String AC_READ = "READ"; + + protected static final float TIME_TO_READ = 1f; + + private static final Class[] scrolls = { + ScrollOfIdentify.class, + ScrollOfMagicMapping.class, + ScrollOfRecharging.class, + ScrollOfRemoveCurse.class, + ScrollOfTeleportation.class, + ScrollOfUpgrade.class, + ScrollOfChallenge.class, + ScrollOfTerror.class, + ScrollOfLullaby.class, + ScrollOfWeaponUpgrade.class, + ScrollOfPsionicBlast.class, + ScrollOfMirrorImage.class + }; + private static final String[] runes = + {"KAUNAN", "SOWILO", "LAGUZ", "YNGVI", "GYFU", "RAIDO", "ISAZ", "MANNAZ", "NAUDIZ", "BERKANAN", "ODAL", "TIWAZ"}; + private static final Integer[] images = { + ItemSpriteSheet.SCROLL_KAUNAN, + ItemSpriteSheet.SCROLL_SOWILO, + ItemSpriteSheet.SCROLL_LAGUZ, + ItemSpriteSheet.SCROLL_YNGVI, + ItemSpriteSheet.SCROLL_GYFU, + ItemSpriteSheet.SCROLL_RAIDO, + ItemSpriteSheet.SCROLL_ISAZ, + ItemSpriteSheet.SCROLL_MANNAZ, + ItemSpriteSheet.SCROLL_NAUDIZ, + ItemSpriteSheet.SCROLL_BERKANAN, + ItemSpriteSheet.SCROLL_ODAL, + ItemSpriteSheet.SCROLL_TIWAZ}; + + private static ItemStatusHandler handler; + + private String rune; + + { + stackable = true; + defaultAction = AC_READ; + } + + @SuppressWarnings("unchecked") + public static void initLabels() { + handler = new ItemStatusHandler( (Class[])scrolls, runes, images ); + } + + public static void save( Bundle bundle ) { + handler.save( bundle ); + } + + @SuppressWarnings("unchecked") + public static void restore( Bundle bundle ) { + handler = new ItemStatusHandler( (Class[])scrolls, runes, images, bundle ); + } + + public Scroll() { + super(); + image = handler.image( this ); + rune = handler.label( this ); + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_READ ); + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_READ )) { + + if (hero.buff( Blindness.class ) != null) { + GLog.w( TXT_BLINDED ); + } else { + curUser = hero; + curItem = detach( hero.belongings.backpack ); + doRead(); + } + + } else { + + super.execute( hero, action ); + + } + } + + abstract protected void doRead(); + + public boolean isKnown() { + return handler.isKnown( this ); + } + + public void setKnown() { + if (!isKnown()) { + handler.know( this ); + } + + Badges.validateAllScrollsIdentified(); + } + + @Override + public Item identify() { + setKnown(); + return super.identify(); + } + + @Override + public String name() { + return isKnown() ? name : "scroll \"" + rune + "\""; + } + + @Override + public String info() { + return isKnown() ? + desc() : + "This parchment is covered with indecipherable writing, and bears a title " + + "of rune " + rune + ". Who knows what it will do when read aloud?"; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return isKnown(); + } + + public static HashSet> getKnown() { + return handler.known(); + } + + public static HashSet> getUnknown() { + return handler.unknown(); + } + + public static boolean allKnown() { + return handler.known().size() == scrolls.length; + } + + @Override + public int price() { + return 15 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfChallenge.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfChallenge.java new file mode 100644 index 0000000000..b15446ed05 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfChallenge.java @@ -0,0 +1,57 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfChallenge extends Scroll { + + { + name = "Scroll of Challenge"; + } + + @Override + protected void doRead() { + + for (Mob mob : Dungeon.level.mobs) { + mob.beckon( curUser.pos ); + } + + GLog.w( "The scroll emits a challenging roar that echoes throughout the dungeon!" ); + setKnown(); + + curUser.sprite.centerEmitter().start( Speck.factory( Speck.SCREAM ), 0.3f, 3 ); + Sample.INSTANCE.play( Assets.SND_CHALLENGE ); + Invisibility.dispel(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "When read aloud, this scroll will unleash a challenging roar " + + "that will awaken all monsters and alert them to the reader's location."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfIdentify.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfIdentify.java new file mode 100644 index 0000000000..b4c8929de2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfIdentify.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.effects.Identification; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag; + +public class ScrollOfIdentify extends InventoryScroll { + + { + name = "Scroll of Identify"; + inventoryTitle = "Select an item to identify"; + mode = WndBag.Mode.UNIDENTIFED; + } + + @Override + protected void onItemSelected( Item item ) { + + curUser.sprite.parent.add( new Identification( curUser.sprite.center().offset( 0, -16 ) ) ); + + item.identify(); + GLog.i( "It is " + item ); + + Badges.validateItemLevelAquired( item ); + } + + @Override + public String desc() { + return + "Permanently reveals all of the secrets of a single item."; + } + + @Override + public int price() { + return isKnown() ? 30 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfLullaby.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfLullaby.java new file mode 100644 index 0000000000..439fd4d6f9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfLullaby.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Sleep; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfLullaby extends Scroll { + + { + name = "Scroll of Lullaby"; + } + + @Override + protected void doRead() { + + curUser.sprite.centerEmitter().start( Speck.factory( Speck.NOTE ), 0.3f, 5 ); + Sample.INSTANCE.play( Assets.SND_LULLABY ); + Invisibility.dispel(); + + int count = 0; + Mob affected = null; + for (Mob mob : Dungeon.level.mobs.toArray( new Mob[0] )) { + if (Level.fieldOfView[mob.pos]) { + Buff.affect( mob, Sleep.class ); + if (mob.buff( Sleep.class ) != null) { + affected = mob; + count++; + } + } + } + + switch (count) { + case 0: + GLog.i( "The scroll utters a soothing melody." ); + break; + case 1: + GLog.i( "The scroll utters a soothing melody and the " + affected.name + " falls asleep!" ); + break; + default: + GLog.i( "The scroll utters a soothing melody and the monsters fall asleep!" ); + } + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "A soothing melody will put all creatures in your field of view into a deep sleep, " + + "giving you a chance to flee or make a surprise attack on them."; + } + + @Override + public int price() { + return isKnown() ? 50 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfMagicMapping.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfMagicMapping.java new file mode 100644 index 0000000000..540139636a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfMagicMapping.java @@ -0,0 +1,103 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.SpellSprite; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfMagicMapping extends Scroll { + + private static final String TXT_LAYOUT = "You are now aware of the level layout."; + + { + name = "Scroll of Magic Mapping"; + } + + @Override + protected void doRead() { + + int length = Level.LENGTH; + int[] map = Dungeon.level.map; + boolean[] mapped = Dungeon.level.mapped; + boolean[] discoverable = Level.discoverable; + + boolean noticed = false; + + for (int i=0; i < length; i++) { + + int terr = map[i]; + + if (discoverable[i]) { + + mapped[i] = true; + if ((Terrain.flags[terr] & Terrain.SECRET) != 0) { + + Level.set( i, Terrain.discover( terr ) ); + GameScene.updateMap( i ); + + if (Dungeon.visible[i]) { + GameScene.discoverTile( i, terr ); + discover( i ); + + noticed = true; + } + } + } + } + Dungeon.observe(); + + GLog.i( TXT_LAYOUT ); + if (noticed) { + Sample.INSTANCE.play( Assets.SND_SECRET ); + } + + SpellSprite.show( curUser, SpellSprite.MAP ); + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "When this scroll is read, an image of crystal clarity will be etched into your memory, " + + "alerting you to the precise layout of the level and revealing all hidden secrets. " + + "The locations of items and creatures will remain unknown."; + } + + @Override + public int price() { + return isKnown() ? 25 * quantity : super.price(); + } + + public static void discover( int cell ) { + CellEmitter.get( cell ).start( Speck.factory( Speck.DISCOVER ), 0.1f, 4 ); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfMirrorImage.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfMirrorImage.java new file mode 100644 index 0000000000..7048b5af1b --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfMirrorImage.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.mobs.npcs.MirrorImage; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Random; + +public class ScrollOfMirrorImage extends Scroll { + + private static final int NIMAGES = 3; + + { + name = "Scroll of Mirror Image"; + } + + @Override + protected void doRead() { + + ArrayList respawnPoints = new ArrayList(); + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + int p = curUser.pos + Level.NEIGHBOURS8[i]; + if (Actor.findChar( p ) == null && (Level.passable[p] || Level.avoid[p])) { + respawnPoints.add( p ); + } + } + + int nImages = NIMAGES; + while (nImages > 0 && respawnPoints.size() > 0) { + int index = Random.index( respawnPoints ); + + MirrorImage mob = new MirrorImage(); + mob.duplicate( curUser ); + GameScene.add( mob ); + WandOfBlink.appear( mob, respawnPoints.get( index ) ); + + respawnPoints.remove( index ); + nImages--; + } + + if (nImages < NIMAGES) { + setKnown(); + } + + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "The incantation on this scroll will create illusionary twins of the reader, which will chase his enemies."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfPsionicBlast.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfPsionicBlast.java new file mode 100644 index 0000000000..82c50cdd79 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfPsionicBlast.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Random; + +public class ScrollOfPsionicBlast extends Scroll { + + { + name = "Scroll of Psionic Blast"; + } + + @Override + protected void doRead() { + + GameScene.flash( 0xFFFFFF ); + + Sample.INSTANCE.play( Assets.SND_BLAST ); + Invisibility.dispel(); + + for (Mob mob : Dungeon.level.mobs.toArray( new Mob[0] )) { + if (Level.fieldOfView[mob.pos]) { + Buff.prolong( mob, Blindness.class, Random.Int( 3, 6 ) ); + mob.damage( Random.IntRange( 1, mob.HT * 2 / 3 ), this ); + } + } + + Buff.prolong( curUser, Blindness.class, Random.Int( 3, 6 ) ); + Dungeon.observe(); + + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "This scroll contains destructive energy, that can be psionically channeled to inflict a " + + "massive damage to all creatures within a field of view. An accompanying flash of light will " + + "temporarily blind everybody in the area of effect including the reader of the scroll."; + } + + @Override + public int price() { + return isKnown() ? 80 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfRecharging.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfRecharging.java new file mode 100644 index 0000000000..c20ca6effe --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfRecharging.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.SpellSprite; +import com.watabou.pixeldungeon.effects.particles.EnergyParticle; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfRecharging extends Scroll { + + { + name = "Scroll of Recharging"; + } + + @Override + protected void doRead() { + + int count = curUser.belongings.charge( true ); + charge( curUser ); + + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + if (count > 0) { + GLog.i( "a surge of energy courses through your pack, recharging your wand" + (count > 1 ? "s" : "") ); + SpellSprite.show( curUser, SpellSprite.CHARGE ); + } else { + GLog.i( "a surge of energy courses through your pack, but nothing happens" ); + } + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "The raw magical power bound up in this parchment will, when released, " + + "recharge all of the reader's wands to full power."; + } + + public static void charge( Hero hero ) { + hero.sprite.centerEmitter().burst( EnergyParticle.FACTORY, 15 ); + } + + @Override + public int price() { + return isKnown() ? 40 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfRemoveCurse.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfRemoveCurse.java new file mode 100644 index 0000000000..93cdd932cc --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfRemoveCurse.java @@ -0,0 +1,98 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Weakness; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfRemoveCurse extends Scroll { + + private static final String TXT_PROCCED = + "Your pack glows with a cleansing light, and a malevolent energy disperses."; + private static final String TXT_NOT_PROCCED = + "Your pack glows with a cleansing light, but nothing happens."; + + { + name = "Scroll of Remove Curse"; + } + + @Override + protected void doRead() { + + new Flare( 6, 32 ).show( curUser.sprite, 2f ) ; + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + boolean procced = uncurse( curUser, curUser.belongings.backpack.items.toArray( new Item[0] ) ); + procced = uncurse( curUser, + curUser.belongings.weapon, + curUser.belongings.armor, + curUser.belongings.ring1, + curUser.belongings.ring2 ) || procced; + + Weakness.detach( curUser, Weakness.class ); + + if (procced) { + GLog.p( TXT_PROCCED ); + } else { + GLog.i( TXT_NOT_PROCCED ); + } + + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "The incantation on this scroll will instantly strip from " + + "the reader's weapon, armor, rings and carried items any evil " + + "enchantments that might prevent the wearer from removing them."; + } + + public static boolean uncurse( Hero hero, Item... items ) { + + boolean procced = false; + for (int i=0; i < items.length; i++) { + Item item = items[i]; + if (item != null && item.cursed) { + item.cursed = false; + procced = true; + } + } + + if (procced) { + hero.sprite.emitter().start( ShadowParticle.UP, 0.05f, 10 ); + } + + return procced; + } + + @Override + public int price() { + return isKnown() ? 30 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfTeleportation.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfTeleportation.java new file mode 100644 index 0000000000..a0e7df6a21 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfTeleportation.java @@ -0,0 +1,91 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfTeleportation extends Scroll { + + public static final String TXT_TELEPORTED = + "In a blink of an eye you were teleported to another location of the level."; + + public static final String TXT_NO_TELEPORT = + "Strong magic aura of this place prevents you from teleporting!"; + + { + name = "Scroll of Teleportation"; + } + + @Override + protected void doRead() { + + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + teleportHero( curUser ); + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + public static void teleportHero( Hero hero ) { + + int count = 10; + int pos; + do { + pos = Dungeon.level.randomRespawnCell(); + if (count-- <= 0) { + break; + } + } while (pos == -1); + + if (pos == -1) { + + GLog.w( TXT_NO_TELEPORT ); + + } else { + + WandOfBlink.appear( hero, pos ); + Dungeon.level.press( pos, hero ); + Dungeon.observe(); + + GLog.i( TXT_TELEPORTED ); + + } + } + + @Override + public String desc() { + return + "The spell on this parchment instantly transports the reader " + + "to a random location on the dungeon level. It can be used " + + "to escape a dangerous situation, but the unlucky reader might " + + "find himself in an even more dangerous place."; + } + + @Override + public int price() { + return isKnown() ? 40 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfTerror.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfTerror.java new file mode 100644 index 0000000000..71c1b7884a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfTerror.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.utils.GLog; + +public class ScrollOfTerror extends Scroll { + + { + name = "Scroll of Terror"; + } + + @Override + protected void doRead() { + + new Flare( 5, 32 ).color( 0xFF0000, true ).show( curUser.sprite, 2f ); + Sample.INSTANCE.play( Assets.SND_READ ); + Invisibility.dispel(); + + int count = 0; + Mob affected = null; + for (Mob mob : Dungeon.level.mobs.toArray( new Mob[0] )) { + if (Level.fieldOfView[mob.pos]) { + Terror terror = Buff.affect( mob, Terror.class, Terror.DURATION ); + terror.source = curUser; + + count++; + affected = mob; + } + } + + switch (count) { + case 0: + GLog.i( "The scroll emits a brilliant flash of red light" ); + break; + case 1: + GLog.i( "The scroll emits a brilliant flash of red light and the " + affected.name + " flees!" ); + break; + default: + GLog.i( "The scroll emits a brilliant flash of red light and the monsters flee!" ); + } + setKnown(); + + curUser.spendAndNext( TIME_TO_READ ); + } + + @Override + public String desc() { + return + "A flash of red light will overwhelm all creatures in your field of view with terror, " + + "and they will turn and flee. Attacking a fleeing enemy will dispel the effect."; + } + + @Override + public int price() { + return isKnown() ? 50 * quantity : super.price(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfUpgrade.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfUpgrade.java new file mode 100644 index 0000000000..ae6bf8050a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfUpgrade.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag; + +public class ScrollOfUpgrade extends InventoryScroll { + + private static final String TXT_LOOKS_BETTER = "your %s certainly looks better now"; + + { + name = "Scroll of Upgrade"; + inventoryTitle = "Select an item to upgrade"; + mode = WndBag.Mode.UPGRADEABLE; + } + + @Override + protected void onItemSelected( Item item ) { + + ScrollOfRemoveCurse.uncurse( Dungeon.hero, item ); + item.upgrade(); + + GLog.p( TXT_LOOKS_BETTER, item.name() ); + + Badges.validateItemLevelAquired( item ); + + upgrade( curUser ); + } + + public static void upgrade( Hero hero ) { + hero.sprite.emitter().start( Speck.factory( Speck.UP ), 0.2f, 3 ); + } + + @Override + public String desc() { + return + "This scroll will upgrade a single item, improving its quality. A wand will " + + "increase in power and in number of charges; a weapon will inflict more damage " + + "or find its mark more frequently; a suit of armor will deflect additional blows; " + + "the effect of a ring on its wearer will intensify. Weapons and armor will also " + + "require less strength to use, and any curses on the item will be lifted."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfWeaponUpgrade.java b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfWeaponUpgrade.java new file mode 100644 index 0000000000..2666c1f79a --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/scrolls/ScrollOfWeaponUpgrade.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.scrolls; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag; + +public class ScrollOfWeaponUpgrade extends InventoryScroll { + + private static final String TXT_LOOKS_BETTER = "your %s certainly looks better now"; + + { + name = "Scroll of Weapon Upgrade"; + inventoryTitle = "Select a weapon to upgrade"; + mode = WndBag.Mode.WEAPON; + } + + @Override + protected void onItemSelected( Item item ) { + + Weapon weapon = (Weapon)item; + + ScrollOfRemoveCurse.uncurse( Dungeon.hero, weapon ); + weapon.upgrade( true ); + + GLog.p( TXT_LOOKS_BETTER, weapon.name() ); + + Badges.validateItemLevelAquired( weapon ); + + curUser.sprite.emitter().start( Speck.factory( Speck.UP ), 0.2f, 3 ); + } + + @Override + public String desc() { + return + "This scroll will upgrade a melee weapon, improving its quality. In contrast to a regular Scroll of Upgrade, " + + "this specialized version will never destroy an enchantment on a weapon. On the contrary, it is able to imbue " + + "an unenchanted weapon with a random enchantment."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/Wand.java b/src/com/watabou/pixeldungeon/items/wands/Wand.java new file mode 100644 index 0000000000..4dbb29176c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/Wand.java @@ -0,0 +1,475 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Invisibility; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.ItemStatusHandler; +import com.watabou.pixeldungeon.items.KindOfWeapon; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.items.rings.RingOfPower.Power; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.CellSelector; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.ui.QuickSlot; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundle; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public abstract class Wand extends KindOfWeapon { + + public static final String AC_ZAP = "ZAP"; + + private static final String TXT_WOOD = "This thin %s wand is warm to the touch. Who knows what it will do when used?"; + private static final String TXT_DAMAGE = "When this wand is used as a melee weapon, its average damage is %d points per hit."; + private static final String TXT_WEAPON = "You can use this wand as a melee weapon."; + + private static final String TXT_FIZZLES = "your wand fizzles; it must be out of charges for now"; + private static final String TXT_SELF_TARGET = "You can't target yourself"; + + private static final float TIME_TO_ZAP = 1f; + + public int maxCharges = initialCharges(); + public int curCharges = maxCharges; + + protected Charger charger; + + private boolean curChargeKnown = false; + + protected boolean hitChars = true; + + private static final Class[] wands = { + WandOfTeleportation.class, + WandOfSlowness.class, + WandOfFirebolt.class, + WandOfPoison.class, + WandOfRegrowth.class, + WandOfBlink.class, + WandOfLightning.class, + WandOfAmok.class, + WandOfTelekinesis.class, + WandOfFlock.class, + WandOfDisintegration.class, + WandOfAvalanche.class + }; + private static final String[] woods = + {"holly", "yew", "ebony", "cherry", "teak", "rowan", "willow", "mahogany", "bamboo", "purpleheart", "oak", "birch"}; + private static final Integer[] images = { + ItemSpriteSheet.WAND_HOLLY, + ItemSpriteSheet.WAND_YEW, + ItemSpriteSheet.WAND_EBONY, + ItemSpriteSheet.WAND_CHERRY, + ItemSpriteSheet.WAND_TEAK, + ItemSpriteSheet.WAND_ROWAN, + ItemSpriteSheet.WAND_WILLOW, + ItemSpriteSheet.WAND_MAHOGANY, + ItemSpriteSheet.WAND_BAMBOO, + ItemSpriteSheet.WAND_PURPLEHEART, + ItemSpriteSheet.WAND_OAK, + ItemSpriteSheet.WAND_BIRCH}; + + private static ItemStatusHandler handler; + + private String wood; + + { + defaultAction = AC_ZAP; + } + + @SuppressWarnings("unchecked") + public static void initWoods() { + handler = new ItemStatusHandler( (Class[])wands, woods, images ); + } + + public static void save( Bundle bundle ) { + handler.save( bundle ); + } + + @SuppressWarnings("unchecked") + public static void restore( Bundle bundle ) { + handler = new ItemStatusHandler( (Class[])wands, woods, images, bundle ); + } + + public Wand() { + super(); + + calculateDamage(); + + try { + image = handler.image( this ); + wood = handler.label( this ); + } catch (Exception e) { + // Wand of Magic Missile + } + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (curCharges > 0 || !curChargeKnown) { + actions.add( AC_ZAP ); + } + if (hero.heroClass != HeroClass.MAGE) { + actions.remove( AC_EQUIP ); + actions.remove( AC_UNEQUIP ); + } + return actions; + } + + @Override + public boolean doUnequip( Hero hero, boolean collect ) { + charger.detach(); + return super.doUnequip(hero, collect); + } + + @Override + public void activate( Hero hero ) { + charge( hero ); + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_ZAP )) { + + curUser = hero; + curItem = this; + GameScene.selectCell( zapper ); + + } else { + + super.execute( hero, action ); + + } + } + + protected abstract void onZap( int cell ); + + @Override + public boolean collect( Bag container ) { + if (super.collect( container )) { + if (container.owner != null) { + charge( container.owner ); + } + return true; + } else { + return false; + } + }; + + public void charge( Char owner ) { + (charger = new Charger()).attachTo( owner ); + } + + @Override + public Item detach( Bag container ) { + stopCharging(); + return super.detach( container ); + } + + public void stopCharging() { + if (charger != null) { + charger.detach(); + charger = null; + } + } + + public int level() { + if (charger != null) { + Power power = charger.target.buff( Power.class ); + return power == null ? level : Math.max( level + power.level, 0 ); + } else { + return level; + } + } + + protected boolean isKnown() { + return handler.isKnown( this ); + } + + public void setKnown() { + if (!isKnown()) { + handler.know( this ); + } + + Badges.validateAllWandsIdentified(); + } + + @Override + public Item identify() { + + setKnown(); + curChargeKnown = true; + super.identify(); + + updateQuickslot(); + + return this; + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder( super.toString() ); + + String status = status(); + if (status != null) { + sb.append( " (" + status + ")" ); + } + + return sb.toString(); + } + + @Override + public String name() { + return isKnown() ? name : wood + " wand"; + } + + @Override + public String info() { + StringBuilder info = new StringBuilder( isKnown() ? desc() : String.format( TXT_WOOD, wood ) ); + if (Dungeon.hero.heroClass == HeroClass.MAGE) { + info.append( "\n\n" ); + if (levelKnown) { + info.append( String.format( TXT_DAMAGE, MIN + (MAX - MIN) / 2 ) ); + } else { + info.append( String.format( TXT_WEAPON ) ); + } + } + return info.toString(); + } + + @Override + public boolean isIdentified() { + return super.isIdentified() && isKnown() && curChargeKnown; + } + + @Override + public String status() { + if (levelKnown) { + return (curChargeKnown ? curCharges : "?") + "/" + maxCharges; + } else { + return null; + } + } + + @Override + public Item upgrade() { + + super.upgrade(); + + updateLevel(); + curCharges = Math.min( curCharges + 1, maxCharges ); + updateQuickslot(); + + return this; + } + + @Override + public Item degrade() { + super.degrade(); + + updateLevel(); + updateQuickslot(); + + return this; + } + + protected void updateLevel() { + maxCharges = Math.min( initialCharges() + level, 9 ); + curCharges = Math.min( curCharges, maxCharges ); + + calculateDamage(); + } + + protected int initialCharges() { + return 2; + } + + private void calculateDamage() { + int tier = 1 + level / 3; + MIN = tier; + MAX = (tier * tier - tier + 10) / 2 + level; + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.blueLight( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + protected void wandUsed() { + curCharges--; + updateQuickslot(); + + curUser.spendAndNext( TIME_TO_ZAP ); + } + + @Override + public Item random() { + if (Random.Float() < 0.5f) { + upgrade(); + if (Random.Float() < 0.15f) { + upgrade(); + } + } + + return this; + } + + public static boolean allKnown() { + return handler.known().size() == wands.length; + } + + @Override + public int price() { + int price = 50; + if (cursed && cursedKnown) { + price /= 2; + } + if (levelKnown) { + if (level > 0) { + price *= (level + 1); + } else if (level < 0) { + price /= (1 - level); + } + } + if (price < 1) { + price = 1; + } + return price; + } + + private static final String MAX_CHARGES = "maxCharges"; + private static final String CUR_CHARGES = "curCharges"; + private static final String CUR_CHARGE_KNOWN = "curChargeKnown"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( MAX_CHARGES, maxCharges ); + bundle.put( CUR_CHARGES, curCharges ); + bundle.put( CUR_CHARGE_KNOWN, curChargeKnown ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + maxCharges = bundle.getInt( MAX_CHARGES ); + curCharges = bundle.getInt( CUR_CHARGES ); + curChargeKnown = bundle.getBoolean( CUR_CHARGE_KNOWN ); + } + + protected static CellSelector.Listener zapper = new CellSelector.Listener() { + + @Override + public void onSelect( Integer target ) { + + if (target != null) { + + if (target == curUser.pos) { + GLog.i( TXT_SELF_TARGET ); + return; + } + + final Wand curWand = (Wand)Wand.curItem; + + curWand.setKnown(); + + final int cell = Ballistica.cast( curUser.pos, target, true, curWand.hitChars ); + curUser.sprite.zap( cell ); + + QuickSlot.target( curItem, Actor.findChar( cell ) ); + + if (curWand.curCharges > 0) { + + curUser.busy(); + + curWand.fx( cell, new Callback() { + @Override + public void call() { + curWand.onZap( cell ); + curWand.wandUsed(); + } + } ); + + Invisibility.dispel(); + + } else { + + curUser.spendAndNext( TIME_TO_ZAP ); + GLog.w( TXT_FIZZLES ); + curWand.levelKnown = true; + + curWand.updateQuickslot(); + } + + } + } + + @Override + public String prompt() { + return "Choose direction to zap"; + } + }; + + protected class Charger extends Buff { + + private static final float TIME_TO_CHARGE = 40f; + + @Override + public boolean attachTo( Char target ) { + super.attachTo( target ); + delay(); + + return true; + } + + @Override + public boolean act() { + + if (curCharges < maxCharges) { + curCharges++; + updateQuickslot(); + } + + delay(); + + return true; + } + + protected void delay() { + float time2charge = ((Hero)target).heroClass == HeroClass.MAGE ? + TIME_TO_CHARGE / (float)Math.sqrt( 1 + level ) : + TIME_TO_CHARGE; + spend( time2charge ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfAmok.java b/src/com/watabou/pixeldungeon/items/wands/WandOfAmok.java new file mode 100644 index 0000000000..b77f27ce73 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfAmok.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Amok; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class WandOfAmok extends Wand { + + { + name = "Wand of Amok"; + } + + @Override + protected void onZap( int cell ) { + Char ch = Actor.findChar( cell ); + if (ch != null) { + + Buff.affect( ch, Amok.class, 3f + level() ); + + } else { + + GLog.i( "nothing happened" ); + + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.purpleLight( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "The purple light from this wand will make the target run amok " + + "attacking random creatures in its vicinity."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfAvalanche.java b/src/com/watabou/pixeldungeon/items/wands/WandOfAvalanche.java new file mode 100644 index 0000000000..45f98d5d03 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfAvalanche.java @@ -0,0 +1,100 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Callback; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; + +public class WandOfAvalanche extends Wand { + + { + name = "Wand of Avalanche"; + hitChars = false; + } + + @Override + protected void onZap( int cell ) { + + Sample.INSTANCE.play( Assets.SND_ROCKS ); + + int level = level(); + + Ballistica.distance = Math.min( Ballistica.distance, 8 + level ); + + int size = 1 + level / 3; + PathFinder.buildDistanceMap( cell, BArray.not( Level.solid, null ), size ); + + for (int i=0; i < Level.LENGTH; i++) { + + int d = PathFinder.distance[i]; + + if (d < Integer.MAX_VALUE) { + + Char ch = Actor.findChar( i ); + if (ch != null) { + + ch.sprite.flash(); + + ch.damage( Random.Int( 2, 6 + (size - d) * 2 ), this ); + + if (ch.isAlive() && Random.Int( 2 + d ) == 0) { + Buff.prolong( ch, Paralysis.class, Random.IntRange( 2, 6 ) ); + } + } + + CellEmitter.get( i ).start( Speck.factory( Speck.ROCK ), 0.07f, 3 + (size - d) ); + Camera.main.shake( 3, 0.07f * (3 + (size - d)) ); + } + } + + if (!curUser.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.WAND, name, Dungeon.depth ) ); + GLog.n( "You killed yourself with your own Wand of Avalanche..." ); + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.earth( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "When a discharge of this wand hits a wall (or any other solid obstacle) it causes " + + "an avalanche of stones, damaging and stunning all creatures in the affected area."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfBlink.java b/src/com/watabou/pixeldungeon/items/wands/WandOfBlink.java new file mode 100644 index 0000000000..1389f81f66 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfBlink.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.utils.Callback; + +public class WandOfBlink extends Wand { + + { + name = "Wand of Blink"; + } + + @Override + protected void onZap( int cell ) { + + int level = level(); + + if (Ballistica.distance > level + 4) { + cell = Ballistica.trace[level + 3]; + } else if (Actor.findChar( cell ) != null && Ballistica.distance > 1) { + cell = Ballistica.trace[Ballistica.distance - 2]; + } + + curUser.sprite.visible = true; + appear( Dungeon.hero, cell ); + Dungeon.observe(); + } + + @Override + protected void fx( int cell, Callback callback ) { + MagicMissile.whiteLight( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + curUser.sprite.visible = false; + } + + public static void appear( Char ch, int pos ) { + + ch.sprite.interruptMotion(); + + ch.move( pos ); + ch.sprite.place( pos ); + + if (ch.invisible == 0) { + ch.sprite.alpha( 0 ); + ch.sprite.parent.add( new AlphaTweener( ch.sprite, 1, 0.4f ) ); + } + + ch.sprite.emitter().start( Speck.factory( Speck.LIGHT ), 0.2f, 3 ); + Sample.INSTANCE.play( Assets.SND_TELEPORT ); + } + + @Override + public String desc() { + return + "This wand will allow you to teleport in the chosen direction. " + + "Creatures and inanimate obstructions will block the teleportation."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfDisintegration.java b/src/com/watabou/pixeldungeon/items/wands/WandOfDisintegration.java new file mode 100644 index 0000000000..de315f354e --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfDisintegration.java @@ -0,0 +1,114 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.DeathRay; +import com.watabou.pixeldungeon.effects.particles.PurpleParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public class WandOfDisintegration extends Wand { + + { + name = "Wand of Disintegration"; + hitChars = false; + } + + @Override + protected void onZap( int cell ) { + + boolean terrainAffected = false; + + int level = level(); + + int maxDistance = distance(); + Ballistica.distance = Math.min( Ballistica.distance, maxDistance ); + + ArrayList chars = new ArrayList(); + + for (int i=1; i < Ballistica.distance; i++) { + + int c = Ballistica.trace[i]; + + Char ch; + if ((ch = Actor.findChar( c )) != null) { + chars.add( ch ); + } + + int terr = Dungeon.level.map[c]; + if (terr == Terrain.DOOR || terr == Terrain.BARRICADE) { + + Level.set( c, Terrain.EMBERS ); + GameScene.updateMap( c ); + terrainAffected = true; + + } else if (terr == Terrain.HIGH_GRASS) { + + Level.set( c, Terrain.GRASS ); + GameScene.updateMap( c ); + terrainAffected = true; + + } + + CellEmitter.center( c ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) ); + } + + if (terrainAffected) { + Dungeon.observe(); + } + + int lvl = level + chars.size(); + int dmgMin = lvl; + int dmgMax = 8 + lvl * lvl / 3; + for (Char ch : chars) { + ch.damage( Random.NormalIntRange( dmgMin, dmgMax ), this ); + ch.sprite.centerEmitter().burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) ); + ch.sprite.flash(); + } + } + + private int distance() { + return level() + 4; + } + + @Override + protected void fx( int cell, Callback callback ) { + + cell = Ballistica.trace[Math.min( Ballistica.distance, distance() ) - 1]; + curUser.sprite.parent.add( new DeathRay( curUser.sprite.center(), DungeonTilemap.tileCenterToWorld( cell ) ) ); + callback.call(); + } + + @Override + public String desc() { + return + "This wand emits a beam of destructive energy, which pierces all creatures in its way. " + + "The more targets it hits, the more damage it inflicts to each of them."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfFirebolt.java b/src/com/watabou/pixeldungeon/items/wands/WandOfFirebolt.java new file mode 100644 index 0000000000..f6b1841c5f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfFirebolt.java @@ -0,0 +1,86 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public class WandOfFirebolt extends Wand { + + { + name = "Wand of Firebolt"; + } + + @Override + protected void onZap( int cell ) { + + int level = level(); + + for (int i=1; i < Ballistica.distance - 1; i++) { + int c = Ballistica.trace[i]; + if (Level.flamable[c]) { + GameScene.add( Blob.seed( c, 1, Fire.class ) ); + } + } + + GameScene.add( Blob.seed( cell, 1, Fire.class ) ); + + Char ch = Actor.findChar( cell ); + if (ch != null) { + + ch.damage( Random.Int( 1, 8 + level * level ), this ); + Buff.affect( ch, Burning.class ).reignite( ch ); + + ch.sprite.emitter().burst( FlameParticle.FACTORY, 5 ); + + if (ch == curUser && !ch.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.WAND, name, Dungeon.depth ) ); + GLog.n( "You killed yourself with your own Wand of Firebolt..." ); + } + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.fire( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "This wand unleashes bursts of magical fire. It will ignite " + + "flammable terrain, and will damage and burn a creature it hits."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfFlock.java b/src/com/watabou/pixeldungeon/items/wands/WandOfFlock.java new file mode 100644 index 0000000000..66f31d5329 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfFlock.java @@ -0,0 +1,151 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.SheepSprite; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.utils.Callback; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; + +public class WandOfFlock extends Wand { + + { + name = "Wand of Flock"; + } + + @Override + protected void onZap( int cell ) { + + int level = level(); + + int n = level + 2; + + if (Actor.findChar( cell ) != null && Ballistica.distance > 2) { + cell = Ballistica.trace[Ballistica.distance - 2]; + } + + boolean[] passable = BArray.or( Level.passable, Level.avoid, null ); + for (Actor actor : Actor.all()) { + if (actor instanceof Char) { + passable[((Char)actor).pos] = false; + } + } + + PathFinder.buildDistanceMap( cell, passable, n ); + int dist = 0; + + if (Actor.findChar( cell ) != null) { + PathFinder.distance[cell] = Integer.MAX_VALUE; + dist = 1; + } + + float lifespan = level + 3; + + sheepLabel: + for (int i=0; i < n; i++) { + do { + for (int j=0; j < Level.LENGTH; j++) { + if (PathFinder.distance[j] == dist) { + + Sheep sheep = new Sheep(); + sheep.lifespan = lifespan; + sheep.pos = j; + GameScene.add( sheep ); + Dungeon.level.mobPress( sheep ); + + CellEmitter.get( j ).burst( Speck.factory( Speck.WOOL ), 4 ); + + PathFinder.distance[j] = Integer.MAX_VALUE; + + continue sheepLabel; + } + } + dist++; + } while (dist < n); + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.wool( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "A flick of this wand summons a flock of magic sheep, creating temporary impenetrable obstacle."; + } + + public static class Sheep extends Mob.NPC { + + private static final String[] QUOTES = {"Baa!", "Baa?", "Baa.", "Baa..."}; + + { + name = "sheep"; + spriteClass = SheepSprite.class; + } + + public float lifespan; + + private boolean initialized = false; + + @Override + protected boolean act() { + if (initialized) { + HP = 0; + + destroy(); + sprite.die(); + + } else { + initialized = true; + spend( lifespan + Random.Float( 2 ) ); + } + return true; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public String description() { + return + "This is a magic sheep. What's so magical about it? You can't kill it. " + + "It will stand there until it magcially fades away, all the while chewing cud with a blank stare."; + } + + @Override + public void interact() { + yell( Random.element( QUOTES ) ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfLightning.java b/src/com/watabou/pixeldungeon/items/wands/WandOfLightning.java new file mode 100644 index 0000000000..21772ebf74 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfLightning.java @@ -0,0 +1,118 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import java.util.ArrayList; +import java.util.HashSet; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Lightning; +import com.watabou.pixeldungeon.effects.particles.SparkParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.traps.LightningTrap; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +public class WandOfLightning extends Wand { + + { + name = "Wand of Lightning"; + } + + private ArrayList affected = new ArrayList(); + + private int[] points = new int[20]; + private int nPoints; + + @Override + protected void onZap( int cell ) { + // The actual effect is processed in "fx" method + + if (!curUser.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.WAND, name, Dungeon.depth ) ); + GLog.n( "You killed yourself with your own Wand of Lightning..." ); + } + } + + private void hit( Char ch, int damage ) { + + if (damage < 1) { + return; + } + + if (ch == Dungeon.hero) { + Camera.main.shake( 2, 0.3f ); + } + + affected.add( ch ); + ch.damage( Level.water[ch.pos] && !ch.flying ? (int)(damage * 2) : damage, LightningTrap.LIGHTNING ); + + ch.sprite.centerEmitter().burst( SparkParticle.FACTORY, 3 ); + ch.sprite.flash(); + + points[nPoints++] = ch.pos; + + HashSet ns = new HashSet(); + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + Char n = Actor.findChar( ch.pos + Level.NEIGHBOURS8[i] ); + if (n != null && !affected.contains( n )) { + ns.add( n ); + } + } + + if (ns.size() > 0) { + hit( Random.element( ns ), Random.Int( damage / 2, damage ) ); + } + } + + @Override + protected void fx( int cell, Callback callback ) { + + nPoints = 0; + points[nPoints++] = Dungeon.hero.pos; + + Char ch = Actor.findChar( cell ); + if (ch != null) { + + affected.clear(); + int lvl = level(); + hit( ch, Random.Int( 5 + lvl / 2, 10 + lvl ) ); + + } else { + + points[nPoints++] = cell; + CellEmitter.center( cell ).burst( SparkParticle.FACTORY, 3 ); + + } + curUser.sprite.parent.add( new Lightning( points, nPoints, callback ) ); + } + + @Override + public String desc() { + return + "This wand conjures forth deadly arcs of electricity, which deal damage " + + "to several creatures standing close to each other."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfMagicMissile.java b/src/com/watabou/pixeldungeon/items/wands/WandOfMagicMissile.java new file mode 100644 index 0000000000..0858ce4e11 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfMagicMissile.java @@ -0,0 +1,153 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndBag; +import com.watabou.utils.Random; + +public class WandOfMagicMissile extends Wand { + + public static final String AC_DISENCHANT = "DISENCHANT"; + + private static final String TXT_SELECT_WAND = "Select a wand to upgrade"; + + private static final String TXT_DISENCHANTED = + "you disenchanted the Wand of Magic Missile and used its essence to upgrade your %s"; + + private static final float TIME_TO_DISENCHANT = 2f; + + private boolean disenchantEquipped; + + { + name = "Wand of Magic Missile"; + image = ItemSpriteSheet.WAND_MAGIC_MISSILE; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (level > 0) { + actions.add( AC_DISENCHANT ); + } + return actions; + } + + @Override + protected void onZap( int cell ) { + + Char ch = Actor.findChar( cell ); + if (ch != null) { + + int level = level(); + + ch.damage( Random.Int( 1, 6 + level * 2 ), this ); + + ch.sprite.burst( 0xFF99CCFF, level / 2 + 2 ); + + if (ch == curUser && !ch.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.WAND, name, Dungeon.depth ) ); + GLog.n( "You killed yourself with your own Wand of Magic Missile..." ); + } + } + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_DISENCHANT )) { + + if (hero.belongings.weapon == this) { + disenchantEquipped = true; + hero.belongings.weapon = null; + updateQuickslot(); + } else { + disenchantEquipped = false; + detach( hero.belongings.backpack ); + } + + curUser = hero; + GameScene.selectItem( itemSelector, WndBag.Mode.WAND, TXT_SELECT_WAND ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + protected boolean isKnown() { + return true; + } + + @Override + public void setKnown() { + } + + protected int initialCharges() { + return 3; + } + + @Override + public String desc() { + return + "This wand launches missiles of pure magical energy, dealing moderate damage to a target creature."; + } + + private final WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + + Sample.INSTANCE.play( Assets.SND_EVOKE ); + ScrollOfUpgrade.upgrade( curUser ); + evoke( curUser ); + + GLog.w( TXT_DISENCHANTED, item.name() ); + + item.upgrade(); + curUser.spendAndNext( TIME_TO_DISENCHANT ); + + Badges.validateItemLevelAquired( item ); + + } else { + if (disenchantEquipped) { + curUser.belongings.weapon = WandOfMagicMissile.this; + WandOfMagicMissile.this.updateQuickslot(); + } else { + collect( curUser.belongings.backpack ); + } + } + } + }; +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfPoison.java b/src/com/watabou/pixeldungeon/items/wands/WandOfPoison.java new file mode 100644 index 0000000000..902ece037b --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfPoison.java @@ -0,0 +1,63 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class WandOfPoison extends Wand { + + { + name = "Wand of Poison"; + } + + @Override + protected void onZap( int cell ) { + Char ch = Actor.findChar( cell ); + if (ch != null) { + + Buff.affect( ch, Poison.class ).set( Poison.durationFactor( ch ) * (1 + 2 * (float)Math.pow( 1.5, level() )) ); + + } else { + + GLog.i( "nothing happened" ); + + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.poison( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "The vile blast of this twisted bit of wood will imbue its target " + + "with a deadly venom. A creature that is poisoned will suffer periodic " + + "damage until the effect ends. The duration of the effect increases " + + "with the level of the staff."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfRegrowth.java b/src/com/watabou/pixeldungeon/items/wands/WandOfRegrowth.java new file mode 100644 index 0000000000..cedf5ab259 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfRegrowth.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Regrowth; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class WandOfRegrowth extends Wand { + + { + name = "Wand of Regrowth"; + } + + @Override + protected void onZap( int cell ) { + + for (int i=1; i < Ballistica.distance-1; i++) { + int p = Ballistica.trace[i]; + int c = Dungeon.level.map[p]; + if (c == Terrain.EMPTY || + c == Terrain.EMBERS || + c == Terrain.EMPTY_DECO) { + + Level.set( p, Terrain.GRASS ); + + } + } + + int c = Dungeon.level.map[cell]; + if (c == Terrain.EMPTY || + c == Terrain.EMBERS || + c == Terrain.EMPTY_DECO || + c == Terrain.GRASS || + c == Terrain.HIGH_GRASS) { + + GameScene.add( Blob.seed( cell, (level() + 2) * 20, Regrowth.class ) ); + + } else { + + GLog.i( "nothing happened" ); + + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.foliage( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "\"When life ceases new life always begins to grow... The eternal cycle always remains!\""; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfSlowness.java b/src/com/watabou/pixeldungeon/items/wands/WandOfSlowness.java new file mode 100644 index 0000000000..80f1104aa5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfSlowness.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Slow; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class WandOfSlowness extends Wand { + + { + name = "Wand of Slowness"; + } + + @Override + protected void onZap( int cell ) { + Char ch = Actor.findChar( cell ); + if (ch != null) { + + Buff.affect( ch, Slow.class, Slow.duration( ch ) / 3 + level() ); + + } else { + + GLog.i( "nothing happened" ); + + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.slowness( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "This wand will cause a creature to move and attack " + + "at half its ordinary speed until the effect ends"; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfTelekinesis.java b/src/com/watabou/pixeldungeon/items/wands/WandOfTelekinesis.java new file mode 100644 index 0000000000..fd816a4c53 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfTelekinesis.java @@ -0,0 +1,170 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.effects.Pushing; +import com.watabou.pixeldungeon.items.Dewdrop; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Heap.Type; +import com.watabou.pixeldungeon.items.potions.PotionOfStrength; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.mechanics.Ballistica; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class WandOfTelekinesis extends Wand { + + private static final String TXT_YOU_NOW_HAVE = "You have magically transported %s into your backpack"; + + { + name = "Wand of Telekinesis"; + hitChars = false; + } + + @Override + protected void onZap( int cell ) { + + boolean mapUpdated = false; + + int maxDistance = level() + 4; + Ballistica.distance = Math.min( Ballistica.distance, maxDistance ); + + Char ch; + Heap heap = null; + + for (int i=1; i < Ballistica.distance; i++) { + + int c = Ballistica.trace[i]; + + int before = Dungeon.level.map[c]; + + if ((ch = Actor.findChar( c )) != null) { + + if (i == Ballistica.distance-1) { + + ch.damage( maxDistance-1 - i, this ); + + } else { + + int next = Ballistica.trace[i + 1]; + if ((Level.passable[next] || Level.avoid[next]) && Actor.findChar( next ) == null) { + + Actor.addDelayed( new Pushing( ch, ch.pos, next ), -1 ); + + ch.pos = next; + Actor.freeCell( next ); + + // Refactoring needed! + if (ch instanceof Mob) { + Dungeon.level.mobPress( (Mob)ch ); + } else { + Dungeon.level.press( ch.pos, ch ); + } + + } else { + + ch.damage( maxDistance-1 - i, this ); + + } + } + } + + if (heap == null && (heap = Dungeon.level.heaps.get( c )) != null) { + switch (heap.type) { + case HEAP: + transport( heap ); + break; + case CHEST: + open( heap ); + break; + default: + } + } + + Dungeon.level.press( c, null ); + if (before == Terrain.OPEN_DOOR && Actor.findChar( c ) == null) { + + Level.set( c, Terrain.DOOR ); + GameScene.updateMap( c ); + + } else if (Level.water[c]) { + + GameScene.ripple( c ); + + } + + if (!mapUpdated && Dungeon.level.map[c] != before) { + mapUpdated = true; + } + } + + if (mapUpdated) { + Dungeon.observe(); + } + } + + private void transport( Heap heap ) { + Item item = heap.pickUp(); + if (item.doPickUp( curUser )) { + + if (item instanceof Dewdrop) { + + } else { + + if ((item instanceof ScrollOfUpgrade && ((ScrollOfUpgrade)item).isKnown()) || + (item instanceof PotionOfStrength && ((PotionOfStrength)item).isKnown())) { + GLog.p( TXT_YOU_NOW_HAVE, item.name() ); + } else { + GLog.i( TXT_YOU_NOW_HAVE, item.name() ); + } + } + + } else { + Dungeon.level.drop( item, curUser.pos ).sprite.drop(); + } + } + + private void open( Heap heap ) { + heap.type = Type.HEAP; + heap.sprite.link(); + heap.sprite.drop(); + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.force( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "Waves of magic force from this wand will affect all cells on their way triggering traps, trampling high vegetation, " + + "opening closed doors and closing open ones. They also push back monsters."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/wands/WandOfTeleportation.java b/src/com/watabou/pixeldungeon/items/wands/WandOfTeleportation.java new file mode 100644 index 0000000000..889d637aa6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/wands/WandOfTeleportation.java @@ -0,0 +1,88 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.wands; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfTeleportation; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Callback; + +public class WandOfTeleportation extends Wand { + + { + name = "Wand of Teleportation"; + } + + @Override + protected void onZap( int cell ) { + + Char ch = Actor.findChar( cell ); + + if (ch == curUser) { + + setKnown(); + ScrollOfTeleportation.teleportHero( curUser ); + + } else if (ch != null) { + + int count = 10; + int pos; + do { + pos = Dungeon.level.randomRespawnCell(); + if (count-- <= 0) { + break; + } + } while (pos == -1); + + if (pos == -1) { + + GLog.w( ScrollOfTeleportation.TXT_NO_TELEPORT ); + + } else { + + ch.pos = pos; + ch.sprite.place( ch.pos ); + ch.sprite.visible = Dungeon.visible[pos]; + GLog.i( curUser.name + " teleported " + ch.name + " to somewhere" ); + + } + + } else { + + GLog.i( "nothing happened" ); + + } + } + + protected void fx( int cell, Callback callback ) { + MagicMissile.coldLight( curUser.sprite.parent, curUser.pos, cell, callback ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public String desc() { + return + "A blast from this wand will teleport a creature against " + + "its will to a random place on the current level."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/Weapon.java b/src/com/watabou/pixeldungeon/items/weapon/Weapon.java new file mode 100644 index 0000000000..9dc7275a1c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/Weapon.java @@ -0,0 +1,221 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.KindOfWeapon; +import com.watabou.pixeldungeon.items.weapon.enchantments.*; +import com.watabou.pixeldungeon.items.weapon.missiles.MissileWeapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Weapon extends KindOfWeapon { + + private static final String TXT_IDENTIFY = + "You are now familiar enough with your %s to identify it. It is %s."; + private static final String TXT_INCOMPATIBLE = + "Interaction of different types of magic has negated the enchantment on this weapon!"; + private static final String TXT_TO_STRING = "%s :%d"; + + public int STR = 10; + public float ACU = 1; // Accuracy modifier + public float DLY = 1f; // Speed modifier + + private int hitsToKnow = 20; + + protected Enchantment enchantment; + + @Override + public void proc( Char attacker, Char defender, int damage ) { + + if (enchantment != null) { + enchantment.proc( this, attacker, defender, damage ); + } + + if (!levelKnown) { + if (--hitsToKnow <= 0) { + levelKnown = true; + GLog.i( TXT_IDENTIFY, name(), toString() ); + Badges.validateItemLevelAquired( this ); + } + } + } + + private static final String ENCHANTMENT = "enchantment"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( ENCHANTMENT, enchantment ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + enchantment = (Enchantment)bundle.get( ENCHANTMENT ); + } + + @Override + public float acuracyFactor( Hero hero ) { + + int encumbrance = STR - hero.STR(); + + if (this instanceof MissileWeapon) { + switch (hero.heroClass) { + case WARRIOR: + encumbrance += 3; + break; + case HUNTRESS: + encumbrance -= 2; + break; + default: + } + } + + return encumbrance > 0 ? (float)(ACU / Math.pow( 1.5, encumbrance )) : ACU; + } + + @Override + public float speedFactor( Hero hero ) { + + int encumrance = STR - hero.STR(); + if (this instanceof MissileWeapon && hero.heroClass == HeroClass.HUNTRESS) { + encumrance -= 2; + } + + return encumrance > 0 ? (float)(DLY * Math.pow( 1.2, encumrance )) : DLY; + } + + @Override + public int damageRoll( Hero hero ) { + + int damage = super.damageRoll( hero ); + + if (hero.usingRanged == (hero.heroClass == HeroClass.HUNTRESS)) { + int exStr = hero.STR() - STR; + if (exStr > 0) { + damage += Random.IntRange( 0, exStr ); + } + } + + return damage; + } + + public Item upgrade( boolean enchant ) { + if (enchantment != null) { + if (!enchant && Random.Int( level ) > 0) { + GLog.w( TXT_INCOMPATIBLE ); + enchant( null ); + } + } else { + if (enchant) { + enchant( Enchantment.random() ); + } + } + + return super.upgrade(); + } + + @Override + public String toString() { + return levelKnown ? Utils.format( TXT_TO_STRING, super.toString(), STR ) : super.toString(); + } + + @Override + public String name() { + return enchantment == null ? super.name() : enchantment.name( super.name() ); + } + + @Override + public Item random() { + if (Random.Float() < 0.4) { + int n = 1; + if (Random.Int( 3 ) == 0) { + n++; + if (Random.Int( 3 ) == 0) { + n++; + } + } + if (Random.Int( 2 ) == 0) { + upgrade( n ); + } else { + degrade( n ); + cursed = true; + } + } + return this; + } + + public Weapon enchant( Enchantment ench ) { + this.enchantment = ench; + return this; + } + + public boolean isEnchanted() { + return enchantment != null; + } + + @Override + public ItemSprite.Glowing glowing() { + return enchantment != null ? enchantment.glowing() : null; + } + + public static abstract class Enchantment implements Bundlable { + + private static final Class[] enchants = new Class[]{ + Fire.class, Poison.class, Death.class, Paralysis.class, Leech.class, + Slow.class, Swing.class, Piercing.class, Instability.class, Horror.class, Luck.class }; + private static final float[] chances= new float[]{ 10, 10, 1, 2, 1, 2, 3, 3, 3, 2, 2 }; + + public abstract boolean proc( Weapon weapon, Char attacker, Char defender, int damage ); + + public String name( String weaponName ) { + return weaponName; + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + } + + @Override + public void storeInBundle( Bundle bundle ) { + } + + public ItemSprite.Glowing glowing() { + return ItemSprite.Glowing.WHITE; + } + + @SuppressWarnings("unchecked") + public static Enchantment random() { + try { + return ((Class)enchants[ Random.chances( chances ) ]).newInstance(); + } catch (Exception e) { + return null; + } + } + + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Death.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Death.java new file mode 100644 index 0000000000..4aad905ea8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Death.java @@ -0,0 +1,70 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Death extends Weapon.Enchantment { + + private static final String TXT_GRIM = "Grim %s"; + + private static ItemSprite.Glowing BLACK = new ItemSprite.Glowing( 0x000000 ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + // lvl 0 - 8% + // lvl 1 ~ 9% + // lvl 2 ~ 10% + int level = Math.max( 0, weapon.level ); + + if (Random.Int( level + 100 ) >= 92) { + + defender.damage( defender.HP, this ); + defender.sprite.emitter().burst( ShadowParticle.UP, 5 ); + + if (!defender.isAlive() && attacker instanceof Hero) { + Badges.validateGrimWeapon(); + } + + return true; + + } else { + + return false; + + } + } + + @Override + public Glowing glowing() { + return BLACK; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_GRIM, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Fire.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Fire.java new file mode 100644 index 0000000000..24184208bb --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Fire.java @@ -0,0 +1,70 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Fire extends Weapon.Enchantment { + + private static final String TXT_BLAZING = "Blazing %s"; + + private static ItemSprite.Glowing ORANGE = new ItemSprite.Glowing( 0xFF4400 ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + // lvl 0 - 33% + // lvl 1 - 50% + // lvl 2 - 60% + int level = Math.max( 0, weapon.level ); + + if (Random.Int( level + 3 ) >= 2) { + + if (Random.Int( 2 ) == 0) { + Buff.affect( defender, Burning.class ).reignite( defender ); + } + defender.damage( Random.Int( 1, level + 2 ), this ); + + defender.sprite.emitter().burst( FlameParticle.FACTORY, level + 1 ); + + return true; + + } else { + + return false; + + } + } + + @Override + public Glowing glowing() { + return ORANGE; + } + + @Override + public String name( String weaponName ) { + return String.format( TXT_BLAZING, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Horror.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Horror.java new file mode 100644 index 0000000000..7e1322c332 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Horror.java @@ -0,0 +1,62 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Terror; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Horror extends Weapon.Enchantment { + + private static final String TXT_ELDRITCH = "Eldritch %s"; + + private static ItemSprite.Glowing GREY = new ItemSprite.Glowing( 0x222222 ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + // lvl 0 - 20% + // lvl 1 - 33% + // lvl 2 - 43% + int level = Math.max( 0, weapon.level ); + + if (Random.Int( level + 5 ) >= 4) { + + Terror terror = Buff.affect( defender, Terror.class, Terror.DURATION ); + terror.source = attacker; + + return true; + } else { + return false; + } + } + + @Override + public Glowing glowing() { + return GREY; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_ELDRITCH, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Instability.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Instability.java new file mode 100644 index 0000000000..f728b44b31 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Instability.java @@ -0,0 +1,45 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.Weapon.Enchantment; +import com.watabou.pixeldungeon.items.weapon.missiles.Boomerang; + +public class Instability extends Weapon.Enchantment { + + private static final String TXT_UNSTABLE = "Unstable %s"; + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + Enchantment ench = random(); + if (weapon instanceof Boomerang) { + while (ench instanceof Piercing || ench instanceof Swing) { + ench = Enchantment.random(); + } + } + return ench.proc( weapon, attacker, defender, damage ); + } + + @Override + public String name( String weaponName) { + return String.format( TXT_UNSTABLE, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Leech.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Leech.java new file mode 100644 index 0000000000..9fca84de06 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Leech.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Leech extends Weapon.Enchantment { + + private static final String TXT_VAMPIRIC = "Vampiric %s"; + + private static ItemSprite.Glowing RED = new ItemSprite.Glowing( 0x660022 ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + + int level = Math.max( 0, weapon.level ); + + // lvl 0 - 33% + // lvl 1 - 43% + // lvl 2 - 50% + int maxValue = damage * (level + 2) / (level + 6); + int effValue = Math.min( Random.IntRange( 0, maxValue ), attacker.HT - attacker.HP ); + + if (effValue > 0) { + + attacker.HP += effValue; + attacker.sprite.emitter().start( Speck.factory( Speck.HEALING ), 0.4f, 1 ); + attacker.sprite.showStatus( CharSprite.POSITIVE, Integer.toString( effValue ) ); + + return true; + + } else { + return false; + } + } + + @Override + public Glowing glowing() { + return RED; + } + + @Override + public String name( String weaponName ) { + return String.format( TXT_VAMPIRIC, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Luck.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Luck.java new file mode 100644 index 0000000000..0d7e265ac9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Luck.java @@ -0,0 +1,57 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; + +public class Luck extends Weapon.Enchantment { + + private static final String TXT_LUCKY = "Lucky %s"; + + private static ItemSprite.Glowing GREEN = new ItemSprite.Glowing( 0x00FF00 ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + int level = Math.max( 0, weapon.level ); + + int dmg = damage; + for (int i=1; i <= level+1; i++) { + dmg = Math.max( dmg, attacker.damageRoll() - i ); + } + + if (dmg > damage) { + defender.damage( dmg - damage, this ); + return true; + } else { + return false; + } + } + + @Override + public String name( String weaponName) { + return String.format( TXT_LUCKY, weaponName ); + } + + @Override + public Glowing glowing() { + return GREEN; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Paralysis.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Paralysis.java new file mode 100644 index 0000000000..5223a9610d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Paralysis.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Paralysis extends Weapon.Enchantment { + + private static final String TXT_STUNNING = "Stunning %s"; + + private static ItemSprite.Glowing YELLOW = new ItemSprite.Glowing( 0xCCAA44 ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + // lvl 0 - 13% + // lvl 1 - 22% + // lvl 2 - 30% + int level = Math.max( 0, weapon.level ); + + if (Random.Int( level + 8 ) >= 7) { + + Buff.prolong( defender, com.watabou.pixeldungeon.actors.buffs.Paralysis.class, + Random.Float( 1, 1.5f + level ) ); + + return true; + } else { + return false; + } + } + + @Override + public Glowing glowing() { + return YELLOW; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_STUNNING, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Piercing.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Piercing.java new file mode 100644 index 0000000000..674ded334c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Piercing.java @@ -0,0 +1,75 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.Weapon.Enchantment; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Random; + +public class Piercing extends Enchantment { + + private static final String TXT_PIERCING = "Piercing %s"; + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + + int level = Math.max( 0, weapon.level ); + + int maxDamage = (int)(damage * Math.pow( 2, -1d / (level + 1) )); + if (maxDamage >= 1) { + + int d = defender.pos - attacker.pos; + int pos = defender.pos + d; + + do { + + Char ch = Actor.findChar( pos ); + if (ch == null) { + break; + } + + int dr = Random.IntRange( 0, ch.dr() ); + int dmg = Random.Int( 1, maxDamage ); + int effectiveDamage = Math.max( dmg - dr, 0 ); + + ch.damage( effectiveDamage, this ); + + ch.sprite.bloodBurstA( attacker.sprite.center(), effectiveDamage ); + ch.sprite.flash(); + + pos += d; + } while (pos >= 0 && pos < Level.LENGTH); + + return true; + + } else { + + return false; + + } + } + + @Override + public String name( String weaponName) { + return String.format( TXT_PIERCING, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Poison.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Poison.java new file mode 100644 index 0000000000..6d2bb5898d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Poison.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Poison extends Weapon.Enchantment { + + private static final String TXT_VENOMOUS = "Venomous %s"; + + private static ItemSprite.Glowing PURPLE = new ItemSprite.Glowing( 0x4400AA ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + // lvl 0 - 33% + // lvl 1 - 50% + // lvl 2 - 60% + int level = Math.max( 0, weapon.level ); + + if (Random.Int( level + 3 ) >= 2) { + + Buff.affect( defender, com.watabou.pixeldungeon.actors.buffs.Poison.class ). + set( com.watabou.pixeldungeon.actors.buffs.Poison.durationFactor( defender ) * (level + 1) ); + + return true; + } else { + return false; + } + } + + @Override + public Glowing glowing() { + return PURPLE; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_VENOMOUS, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Slow.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Slow.java new file mode 100644 index 0000000000..b34fefd8fd --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Slow.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite.Glowing; +import com.watabou.utils.Random; + +public class Slow extends Weapon.Enchantment { + + private static final String TXT_CHILLING = "Chilling %s"; + + private static ItemSprite.Glowing BLUE = new ItemSprite.Glowing( 0x0044FF ); + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + // lvl 0 - 25% + // lvl 1 - 40% + // lvl 2 - 50% + int level = Math.max( 0, weapon.level ); + + if (Random.Int( level + 4 ) >= 3) { + + Buff.prolong( defender, com.watabou.pixeldungeon.actors.buffs.Slow.class, + Random.Float( 1, 1.5f + level ) ); + + return true; + } else { + return false; + } + } + + @Override + public Glowing glowing() { + return BLUE; + } + + @Override + public String name( String weaponName) { + return String.format( TXT_CHILLING, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/enchantments/Swing.java b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Swing.java new file mode 100644 index 0000000000..3719c09883 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/enchantments/Swing.java @@ -0,0 +1,74 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.enchantments; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.Weapon.Enchantment; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Random; + +public class Swing extends Enchantment { + + private static final String TXT_WILD = "Wild %s"; + + @Override + public boolean proc( Weapon weapon, Char attacker, Char defender, int damage ) { + + int level = Math.max( 0, weapon.level ); + + int maxDamage = (int)(damage * Math.pow( 2, -1d / (level + 1) )); + if (maxDamage >= 1) { + + int p = attacker.pos; + int[] neighbours = { + p+1, p-1, p+Level.WIDTH, p-Level.WIDTH, + p+1+Level.WIDTH, p+1-Level.WIDTH, p-1+Level.WIDTH, p-1-Level.WIDTH}; + + for (int n : neighbours) { + Char ch = Actor.findChar( n ); + if (ch != null && ch != defender && ch.isAlive()) { + + int dr = Random.IntRange( 0, ch.dr() ); + int dmg = Random.Int( 1, maxDamage ); + int effectiveDamage = Math.max( dmg - dr, 0 ); + + ch.damage( effectiveDamage, this ); + + ch.sprite.bloodBurstA( attacker.sprite.center(), effectiveDamage ); + ch.sprite.flash(); + + } + } + + return true; + + } else { + + return false; + + } + } + + @Override + public String name( String weaponName) { + return String.format( TXT_WILD, weaponName ); + } + +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/BattleAxe.java b/src/com/watabou/pixeldungeon/items/weapon/melee/BattleAxe.java new file mode 100644 index 0000000000..e658df7a8e --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/BattleAxe.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class BattleAxe extends MeleeWeapon { + + { + name = "battle axe"; + image = ItemSpriteSheet.BATTLE_AXE; + } + + public BattleAxe() { + super( 4, 1.2f, 1f ); + } + + @Override + public String desc() { + return "The enormous steel head of this battle axe puts considerable heft behind each stroke."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Dagger.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Dagger.java new file mode 100644 index 0000000000..baab2fd282 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Dagger.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Dagger extends MeleeWeapon { + + { + name = "dagger"; + image = ItemSpriteSheet.DAGGER; + } + + public Dagger() { + super( 1, 1.2f, 1f ); + } + + @Override + public String desc() { + return "A simple iron dagger with a well worn wooden handle."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Glaive.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Glaive.java new file mode 100644 index 0000000000..965855c22c --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Glaive.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Glaive extends MeleeWeapon { + + { + name = "glaive"; + image = ItemSpriteSheet.GLAIVE; + } + + public Glaive() { + super( 5, 1f, 1f ); + } + + @Override + public String desc() { + return "A polearm consisting of a sword blade on the end of a pole."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Knuckles.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Knuckles.java new file mode 100644 index 0000000000..5f0cdf33e9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Knuckles.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Knuckles extends MeleeWeapon { + + { + name = "knuckleduster"; + image = ItemSpriteSheet.KNUCKLEDUSTER; + } + + public Knuckles() { + super( 1, 1f, 0.5f ); + } + + @Override + public String desc() { + return "A piece of iron shaped to fit around the knuckles."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Longsword.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Longsword.java new file mode 100644 index 0000000000..9ea294590b --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Longsword.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Longsword extends MeleeWeapon { + + { + name = "longsword"; + image = ItemSpriteSheet.LONG_SWORD; + } + + public Longsword() { + super( 4, 1f, 1f ); + } + + @Override + public String desc() { + return "This towering blade inflicts heavy damage by investing its heft into every cut."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Mace.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Mace.java new file mode 100644 index 0000000000..4e4d987175 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Mace.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Mace extends MeleeWeapon { + + { + name = "mace"; + image = ItemSpriteSheet.MACE; + } + + public Mace() { + super( 3, 1f, 0.8f ); + } + + @Override + public String desc() { + return "The iron head of this weapon inflicts substantial damage."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/MeleeWeapon.java b/src/com/watabou/pixeldungeon/items/weapon/melee/MeleeWeapon.java new file mode 100644 index 0000000000..13347d3c68 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/MeleeWeapon.java @@ -0,0 +1,184 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Random; + +public class MeleeWeapon extends Weapon { + + private int tier; + + public MeleeWeapon( int tier, float acu, float dly ) { + super(); + + this.tier = tier; + + ACU = acu; + DLY = dly; + + STR = typicalSTR(); + + MIN = min(); + MAX = max(); + } + + private int min() { + return tier; + } + + private int max() { + return (int)((tier * tier - tier + 10) / ACU * DLY); + } + + @Override + public Item upgrade() { + return upgrade( false ); + } + + public Item upgrade( boolean enchant ) { + STR--; + MIN++; + MAX += tier; + + return super.upgrade( enchant ); + } + + public Item safeUpgrade() { + return upgrade( enchantment != null ); + } + + @Override + public Item degrade() { + STR++; + MIN--; + MAX -= tier; + return super.degrade(); + } + + public int typicalSTR() { + return 8 + tier * 2; + } + + @Override + public String info() { + + final String p = "\n\n"; + + StringBuilder info = new StringBuilder( desc() ); + + String quality = levelKnown && level != 0 ? (level > 0 ? "upgraded" : "degraded") : ""; + info.append( p ); + info.append( "This " + name + " is " + Utils.indefinite( quality ) ); + info.append( " tier-" + tier + " melee weapon. " ); + + if (levelKnown) { + info.append( "Its average damage is " + (MIN + (MAX - MIN) / 2) + " points per hit. " ); + } else { + info.append( + "Its typical average damage is " + (min() + (max() - min()) / 2) + " points per hit " + + "and usually it requires " + typicalSTR() + " points of strength. " ); + if (typicalSTR() > Dungeon.hero.STR()) { + info.append( "Probably this weapon is too heavy for you. " ); + } + } + + if (DLY != 1f) { + info.append( "This is a rather " + (DLY < 1f ? "fast" : "slow") ); + if (ACU != 1f) { + if ((ACU > 1f) == (DLY < 1f)) { + info.append( " and "); + } else { + info.append( " but "); + } + info.append( ACU > 1f ? "accurate" : "inaccurate" ); + } + info.append( " weapon. "); + } else if (ACU != 1f) { + info.append( "This is a rather " + (ACU > 1f ? "accurate" : "inaccurate") + " weapon. " ); + } + + if (enchantment != null) { + info.append( "It is enchanted." ); + } + + if (levelKnown && Dungeon.hero.belongings.backpack.items.contains( this )) { + if (STR > Dungeon.hero.STR()) { + info.append( p ); + info.append( + "Because of your inadequate strength the accuracy and speed " + + "of your attack with this " + name + " is decreased." ); + } + if (STR < Dungeon.hero.STR()) { + info.append( p ); + info.append( + "Because of your excess strength the damage " + + "of your attack with this " + name + " is increased." ); + } + } + + if (isEquipped( Dungeon.hero )) { + info.append( p ); + info.append( "You hold the " + name + " at the ready" + + (cursed ? ", and because it is cursed, you are powerless to let go." : ".") ); + } else { + if (cursedKnown && cursed) { + info.append( p ); + info.append( "You can feel a malevolent magic lurking within " + name +"." ); + } + } + + return info.toString(); + } + + @Override + public int price() { + int price = 20 * (1 << (tier - 1)); + if (enchantment != null) { + price *= 1.5; + } + if (cursed && cursedKnown) { + price /= 2; + } + if (levelKnown) { + if (level > 0) { + price *= (level + 1); + } else if (level < 0) { + price /= (1 - level); + } + } + if (price < 1) { + price = 1; + } + return price; + } + + @Override + public Item random() { + super.random(); + + if (Random.Int( 10 + level ) == 0) { + enchant( Enchantment.random() ); + } + + return this; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Quarterstaff.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Quarterstaff.java new file mode 100644 index 0000000000..6458a09a5f --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Quarterstaff.java @@ -0,0 +1,38 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Quarterstaff extends MeleeWeapon { + + { + name = "quarterstaff"; + image = ItemSpriteSheet.QUARTERSTAFF; + } + + public Quarterstaff() { + super( 2, 1f, 1f ); + } + + @Override + public String desc() { + // TODO Auto-generated method stub + return "A staff of hardwood, its ends are shod with iron."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/ShortSword.java b/src/com/watabou/pixeldungeon/items/weapon/melee/ShortSword.java new file mode 100644 index 0000000000..1531b3254d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/ShortSword.java @@ -0,0 +1,129 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.items.weapon.missiles.Boomerang; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag; + +public class ShortSword extends MeleeWeapon { + + public static final String AC_REFORGE = "REFORGE"; + + private static final String TXT_SELECT_WEAPON = "Select a weapon to upgrade"; + + private static final String TXT_REFORGED = + "you reforged the short sword to upgrade your %s"; + private static final String TXT_NOT_BOOMERANG = + "you can't upgrade a boomerang this way"; + + private static final float TIME_TO_REFORGE = 2f; + + private boolean equipped; + + { + name = "short sword"; + image = ItemSpriteSheet.SHORT_SWORD; + } + + public ShortSword() { + super( 1, 1f, 1f ); + + STR = 11; + MAX = 12; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (level > 0) { + actions.add( AC_REFORGE ); + } + return actions; + } + + @Override + public void execute( Hero hero, String action ) { + if (action == AC_REFORGE) { + + if (hero.belongings.weapon == this) { + equipped = true; + hero.belongings.weapon = null; + } else { + equipped = false; + detach( hero.belongings.backpack ); + } + + curUser = hero; + + GameScene.selectItem( itemSelector, WndBag.Mode.WEAPON, TXT_SELECT_WEAPON ); + + } else { + + super.execute( hero, action ); + + } + } + + @Override + public String desc() { + return + "It is indeed quite short, just a few inches longer, than a dagger."; + } + + private final WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null && !(item instanceof Boomerang)) { + + Sample.INSTANCE.play( Assets.SND_EVOKE ); + ScrollOfUpgrade.upgrade( curUser ); + evoke( curUser ); + + GLog.w( TXT_REFORGED, item.name() ); + + ((MeleeWeapon)item).safeUpgrade(); + curUser.spendAndNext( TIME_TO_REFORGE ); + + Badges.validateItemLevelAquired( item ); + + } else { + + if (item instanceof Boomerang) { + GLog.w( TXT_NOT_BOOMERANG ); + } + + if (equipped) { + curUser.belongings.weapon = ShortSword.this; + } else { + collect( curUser.belongings.backpack ); + } + } + } + }; +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Spear.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Spear.java new file mode 100644 index 0000000000..0a9e711bf6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Spear.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Spear extends MeleeWeapon { + + { + name = "spear"; + image = ItemSpriteSheet.SPEAR; + } + + public Spear() { + super( 2, 1f, 1.5f ); + } + + @Override + public String desc() { + return "A slender wooden rod tipped with sharpened iron."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/Sword.java b/src/com/watabou/pixeldungeon/items/weapon/melee/Sword.java new file mode 100644 index 0000000000..14b015f382 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/Sword.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Sword extends MeleeWeapon { + + { + name = "sword"; + image = ItemSpriteSheet.SWORD; + } + + public Sword() { + super( 3, 1f, 1f ); + } + + @Override + public String desc() { + return "The razor-sharp length of steel blade shines reassuringly."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/melee/WarHammer.java b/src/com/watabou/pixeldungeon/items/weapon/melee/WarHammer.java new file mode 100644 index 0000000000..3a8a3258d7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/melee/WarHammer.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.melee; + +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class WarHammer extends MeleeWeapon { + + { + name = "war hammer"; + image = ItemSpriteSheet.WAR_HAMMER; + } + + public WarHammer() { + super( 5, 1.2f, 1f ); + } + + @Override + public String desc() { + return + "Few creatures can withstand the crushing blow of this towering mass of lead and steel, " + + "but only the strongest of adventurers can use it effectively."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/Boomerang.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/Boomerang.java new file mode 100644 index 0000000000..4d1bb886c2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/Boomerang.java @@ -0,0 +1,106 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.enchantments.Piercing; +import com.watabou.pixeldungeon.items.weapon.enchantments.Swing; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.sprites.MissileSprite; + +public class Boomerang extends MissileWeapon { + + { + name = "boomerang"; + image = ItemSpriteSheet.BOOMERANG; + + STR = 10; + + MIN = 1; + MAX = 4; + + stackable = false; + } + + @Override + public boolean isUpgradable() { + return true; + } + + @Override + public Item upgrade() { + return upgrade( false ); + } + + @Override + public Item upgrade( boolean enchant ) { + MIN += 1; + MAX += 2; + super.upgrade( enchant ); + + updateQuickslot(); + + return this; + } + + @Override + public Item degrade() { + MIN -= 1; + MAX -= 2; + return super.degrade(); + } + + @Override + public Weapon enchant( Enchantment ench ) { + while (ench instanceof Piercing || ench instanceof Swing) { + ench = Enchantment.random(); + } + + return super.enchant( ench ); + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + super.proc( attacker, defender, damage ); + if (attacker instanceof Hero && ((Hero)attacker).usingRanged) { + circleBack( defender.pos, (Hero)attacker ); + } + } + + @Override + protected void miss( int cell ) { + circleBack( cell, curUser ); + } + + private void circleBack( int from, Hero owner ) { + if (!collect( curUser.belongings.backpack )) { + Dungeon.level.drop( this, owner.pos ).sprite.drop(); + } + ((MissileSprite)curUser.sprite.parent.recycle( MissileSprite.class )). + reset( from, curUser.pos, curItem, null ); + } + + @Override + public String desc() { + return + "Thrown to the enemy this flat curved wooden missile will return to the hands of its thrower."; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/CurareDart.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/CurareDart.java new file mode 100644 index 0000000000..409be7da68 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/CurareDart.java @@ -0,0 +1,73 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Paralysis; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class CurareDart extends MissileWeapon { + + public static final float DURATION = 3f; + + { + name = "curare dart"; + image = ItemSpriteSheet.CURARE_DART; + + STR = 14; + + MIN = 1; + MAX = 3; + } + + public CurareDart() { + this( 1 ); + } + + public CurareDart( int number ) { + super(); + quantity = number; + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + Buff.prolong( defender, Paralysis.class, DURATION ); + super.proc( attacker, defender, damage ); + } + + @Override + public String desc() { + return + "These little evil darts don't do much damage but they can paralyze " + + "the target leaving it helpless and motionless for some time."; + } + + @Override + public Item random() { + quantity = Random.Int( 2, 5 ); + return this; + } + + @Override + public int price() { + return 12 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/Dart.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/Dart.java new file mode 100644 index 0000000000..bebc5790de --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/Dart.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class Dart extends MissileWeapon { + + { + name = "dart"; + image = ItemSpriteSheet.DART; + + MIN = 1; + MAX = 4; + } + + public Dart() { + this( 1 ); + } + + public Dart( int number ) { + super(); + quantity = number; + } + + @Override + public String desc() { + return + "These simple metal spikes are weighted to fly true and " + + "sting their prey with a flick of the wrist."; + } + + @Override + public Item random() { + quantity = Random.Int( 5, 15 ); + return this; + } + + @Override + public int price() { + return quantity * 2; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/IncendiaryDart.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/IncendiaryDart.java new file mode 100644 index 0000000000..d8bbc88c84 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/IncendiaryDart.java @@ -0,0 +1,93 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Burning; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class IncendiaryDart extends MissileWeapon { + + { + name = "incendiary dart"; + image = ItemSpriteSheet.INCENDIARY_DART; + + STR = 12; + + MIN = 1; + MAX = 2; + } + + public IncendiaryDart() { + this( 1 ); + } + + public IncendiaryDart( int number ) { + super(); + quantity = number; + } + + @Override + protected void onThrow( int cell ) { + Char enemy = Actor.findChar( cell ); + if (enemy == null || enemy == curUser) { + if (Level.flamable[cell]) { + GameScene.add( Blob.seed( cell, 4, Fire.class ) ); + } else { + super.onThrow( cell ); + } + } else { + if (!curUser.shoot( enemy, this )) { + Dungeon.level.drop( this, cell ).sprite.drop(); + } + } + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + Buff.affect( defender, Burning.class ).reignite( defender ); + super.proc( attacker, defender, damage ); + } + + @Override + public String desc() { + return + "The spike on each of these darts is designed to pin it to its target " + + "while the unstable compounds strapped to its length burst into brilliant flames."; + } + + @Override + public Item random() { + quantity = Random.Int( 3, 6 ); + return this; + } + + @Override + public int price() { + return 10 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/Javelin.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/Javelin.java new file mode 100644 index 0000000000..6fb7cc7bf1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/Javelin.java @@ -0,0 +1,71 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class Javelin extends MissileWeapon { + + { + name = "javelin"; + image = ItemSpriteSheet.JAVELIN; + + STR = 15; + + MIN = 2; + MAX = 15; + } + + public Javelin() { + this( 1 ); + } + + public Javelin( int number ) { + super(); + quantity = number; + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + super.proc( attacker, defender, damage ); + Buff.prolong( defender, Cripple.class, Cripple.DURATION ); + } + + @Override + public String desc() { + return + "This length of metal is weighted to keep the spike " + + "at its tip foremost as it sails through the air."; + } + + @Override + public Item random() { + quantity = Random.Int( 5, 15 ); + return this; + } + + @Override + public int price() { + return 15 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/MissileWeapon.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/MissileWeapon.java new file mode 100644 index 0000000000..62c3be8058 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/MissileWeapon.java @@ -0,0 +1,144 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.windows.WndOptions; + +public class MissileWeapon extends Weapon { + + private static final String TXT_MISSILES = "Missile weapon"; + private static final String TXT_YES = "Yes, I know what I'm doing"; + private static final String TXT_NO = "No, I changed my mind"; + private static final String TXT_R_U_SURE = + "Do you really want to equip it as a melee weapon?"; + + { + stackable = true; + levelKnown = true; + defaultAction = AC_THROW; + } + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (hero.heroClass != HeroClass.HUNTRESS && hero.heroClass != HeroClass.ROGUE) { + actions.remove( AC_EQUIP ); + actions.remove( AC_UNEQUIP ); + } + return actions; + } + + @Override + protected void onThrow( int cell ) { + Char enemy = Actor.findChar( cell ); + if (enemy == null || enemy == curUser) { + super.onThrow( cell ); + } else { + if (!curUser.shoot( enemy, this )) { + miss( cell ); + } + } + } + + protected void miss( int cell ) { + super.onThrow( cell ); + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + + super.proc( attacker, defender, damage ); + + Hero hero = (Hero)attacker; + if (!hero.usingRanged && stackable) { + if (quantity == 1) { + doUnequip( hero, false ); + } else { + detach( null ); + } + } + } + + @Override + public boolean doEquip( final Hero hero ) { + GameScene.show( + new WndOptions( TXT_MISSILES, TXT_R_U_SURE, TXT_YES, TXT_NO ) { + @Override + protected void onSelect(int index) { + if (index == 0) { + MissileWeapon.super.doEquip( hero ); + } + }; + } + ); + + return false; + } + + @Override + public Item random() { + return this; + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public String info() { + + StringBuilder info = new StringBuilder( desc() ); + + info.append( "\n\nAverage damage of this weapon equals to " + (MIN + (MAX - MIN) / 2) + " points per hit. " ); + + if (Dungeon.hero.belongings.backpack.items.contains( this )) { + if (STR > Dungeon.hero.STR()) { + info.append( + "Because of your inadequate strength the accuracy and speed " + + "of your attack with this " + name + " is decreased." ); + } + if (STR < Dungeon.hero.STR()) { + info.append( + "Because of your excess strength the damage " + + "of your attack with this " + name + " is increased." ); + } + } + + if (isEquipped( Dungeon.hero )) { + info.append( "\n\nYou hold the " + name + " at the ready." ); + } + + return info.toString(); + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/Shuriken.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/Shuriken.java new file mode 100644 index 0000000000..a5f37a73c8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/Shuriken.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class Shuriken extends MissileWeapon { + + { + name = "shuriken"; + image = ItemSpriteSheet.SHURIKEN; + + STR = 13; + + MIN = 2; + MAX = 6; + + DLY = 0.5f; + } + + public Shuriken() { + this( 1 ); + } + + public Shuriken( int number ) { + super(); + quantity = number; + } + + @Override + public String desc() { + return + "Star-shaped pieces of metal with razor-sharp blades do significant damage " + + "when they hit a target. They can be thrown at very high rate."; + } + + @Override + public Item random() { + quantity = Random.Int( 5, 15 ); + return this; + } + + @Override + public int price() { + return 15 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/items/weapon/missiles/Tamahawk.java b/src/com/watabou/pixeldungeon/items/weapon/missiles/Tamahawk.java new file mode 100644 index 0000000000..baa9cd1a8d --- /dev/null +++ b/src/com/watabou/pixeldungeon/items/weapon/missiles/Tamahawk.java @@ -0,0 +1,71 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.items.weapon.missiles; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class Tamahawk extends MissileWeapon { + + { + name = "tomahawk"; + image = ItemSpriteSheet.TOMAHAWK; + + STR = 17; + + MIN = 4; + MAX = 20; + } + + public Tamahawk() { + this( 1 ); + } + + public Tamahawk( int number ) { + super(); + quantity = number; + } + + @Override + public void proc( Char attacker, Char defender, int damage ) { + super.proc( attacker, defender, damage ); + Buff.affect( defender, Bleeding.class ).set( damage ); + } + + @Override + public String desc() { + return + "This throwing axe is not that heavy, but it still " + + "requires significant strength to be used effectively."; + } + + @Override + public Item random() { + quantity = Random.Int( 5, 12 ); + return this; + } + + @Override + public int price() { + return 20 * quantity; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/CavesBossLevel.java b/src/com/watabou/pixeldungeon/levels/CavesBossLevel.java new file mode 100644 index 0000000000..0d6ef21eb1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/CavesBossLevel.java @@ -0,0 +1,299 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.Scene; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class CavesBossLevel extends Level { + + { + color1 = 0x534f3e; + color2 = 0xb9d661; + + viewDistance = 6; + } + + private static final int ROOM_LEFT = WIDTH / 2 - 2; + private static final int ROOM_RIGHT = WIDTH / 2 + 2; + private static final int ROOM_TOP = HEIGHT / 2 - 2; + private static final int ROOM_BOTTOM = HEIGHT / 2 + 2; + + private int arenaDoor; + private boolean enteredArena = false; + private boolean keyDropped = false; + + @Override + public String tilesTex() { + return Assets.TILES_CAVES; + } + + @Override + public String waterTex() { + return Assets.WATER_CAVES; + } + + private static final String DOOR = "door"; + private static final String ENTERED = "entered"; + private static final String DROPPED = "droppped"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( DOOR, arenaDoor ); + bundle.put( ENTERED, enteredArena ); + bundle.put( DROPPED, keyDropped ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + arenaDoor = bundle.getInt( DOOR ); + enteredArena = bundle.getBoolean( ENTERED ); + keyDropped = bundle.getBoolean( DROPPED ); + } + + @Override + protected boolean build() { + + int topMost = Integer.MAX_VALUE; + + for (int i=0; i < 8; i++) { + int left, right, top, bottom; + if (Random.Int( 2 ) == 0) { + left = Random.Int( 1, ROOM_LEFT - 3 ); + right = ROOM_RIGHT + 3; + } else { + left = ROOM_LEFT - 3; + right = Random.Int( ROOM_RIGHT + 3, WIDTH - 1 ); + } + if (Random.Int( 2 ) == 0) { + top = Random.Int( 2, ROOM_TOP - 3 ); + bottom = ROOM_BOTTOM + 3; + } else { + top = ROOM_LEFT - 3; + bottom = Random.Int( ROOM_TOP + 3, HEIGHT - 1 ); + } + + Painter.fill( this, left, top, right - left + 1, bottom - top + 1, Terrain.EMPTY ); + + if (top < topMost) { + topMost = top; + exit = Random.Int( left, right ) + (top - 1) * WIDTH; + } + } + + map[exit] = Terrain.LOCKED_EXIT; + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 6 ) == 0) { + map[i] = Terrain.INACTIVE_TRAP; + } + } + + Painter.fill( this, ROOM_LEFT - 1, ROOM_TOP - 1, + ROOM_RIGHT - ROOM_LEFT + 3, ROOM_BOTTOM - ROOM_TOP + 3, Terrain.WALL ); + Painter.fill( this, ROOM_LEFT, ROOM_TOP + 1, + ROOM_RIGHT - ROOM_LEFT + 1, ROOM_BOTTOM - ROOM_TOP, Terrain.EMPTY ); + + Painter.fill( this, ROOM_LEFT, ROOM_TOP, + ROOM_RIGHT - ROOM_LEFT + 1, 1, Terrain.TOXIC_TRAP ); + + arenaDoor = Random.Int( ROOM_LEFT, ROOM_RIGHT ) + (ROOM_BOTTOM + 1) * WIDTH; + map[arenaDoor] = Terrain.DOOR; + + entrance = Random.Int( ROOM_LEFT + 1, ROOM_RIGHT - 1 ) + + Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * WIDTH; + map[entrance] = Terrain.ENTRANCE; + + boolean[] patch = Patch.generate( 0.45f, 6 ); + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && patch[i]) { + map[i] = Terrain.WATER; + } + } + + return true; + } + + @Override + protected void decorate() { + + for (int i=WIDTH + 1; i < LENGTH - WIDTH; i++) { + if (map[i] == Terrain.EMPTY) { + int n = 0; + if (map[i+1] == Terrain.WALL) { + n++; + } + if (map[i-1] == Terrain.WALL) { + n++; + } + if (map[i+WIDTH] == Terrain.WALL) { + n++; + } + if (map[i-WIDTH] == Terrain.WALL) { + n++; + } + if (Random.Int( 8 ) <= n) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.WALL && Random.Int( 8 ) == 0) { + map[i] = Terrain.WALL_DECO; + } + } + + int sign; + do { + sign = Random.Int( ROOM_LEFT, ROOM_RIGHT ) + Random.Int( ROOM_TOP, ROOM_BOTTOM ) * WIDTH; + } while (sign == entrance); + map[sign] = Terrain.SIGN; + } + + @Override + protected void createMobs() { + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = Random.IntRange( ROOM_LEFT, ROOM_RIGHT ) + Random.IntRange( ROOM_TOP + 1, ROOM_BOTTOM ) * WIDTH; + } while (pos == entrance || map[pos] == Terrain.SIGN); + drop( item, pos ).type = Heap.Type.SKELETON; + } + } + + @Override + public int randomRespawnCell() { + return -1; + } + + @Override + public void press( int cell, Char hero ) { + + super.press( cell, hero ); + + if (!enteredArena && outsideEntraceRoom( cell ) && hero == Dungeon.hero) { + + enteredArena = true; + + Mob boss = Bestiary.mob( Dungeon.depth ); + boss.state = Mob.State.HUNTING; + do { + boss.pos = Random.Int( LENGTH ); + } while ( + !passable[boss.pos] || + !outsideEntraceRoom( boss.pos ) || + Dungeon.visible[boss.pos]); + GameScene.add( boss ); + + set( arenaDoor, Terrain.WALL ); + GameScene.updateMap( arenaDoor ); + Dungeon.observe(); + + CellEmitter.get( arenaDoor ).start( Speck.factory( Speck.ROCK ), 0.07f, 10 ); + Camera.main.shake( 3, 0.7f ); + Sample.INSTANCE.play( Assets.SND_ROCKS ); + } + } + + @Override + public Heap drop( Item item, int cell ) { + + if (!keyDropped && item instanceof SkeletonKey) { + + keyDropped = true; + + CellEmitter.get( arenaDoor ).start( Speck.factory( Speck.ROCK ), 0.07f, 10 ); + + set( arenaDoor, Terrain.EMPTY_DECO ); + GameScene.updateMap( arenaDoor ); + Dungeon.observe(); + } + + return super.drop( item, cell ); + } + + private boolean outsideEntraceRoom( int cell ) { + int cx = cell % WIDTH; + int cy = cell / WIDTH; + return cx < ROOM_LEFT-1 || cx > ROOM_RIGHT+1 || cy < ROOM_TOP-1 || cy > ROOM_BOTTOM+1; + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.GRASS: + return "Fluorescent moss"; + case Terrain.HIGH_GRASS: + return "Fluorescent mushrooms"; + case Terrain.WATER: + return "Freezing cold water."; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc( int tile ) { + switch (tile) { + case Terrain.ENTRANCE: + return "The ladder leads up to the upper depth."; + case Terrain.EXIT: + return "The ladder leads down to the lower depth."; + case Terrain.HIGH_GRASS: + return "Huge mushrooms block the view."; + case Terrain.WALL_DECO: + return "A vein of some ore is visible on the wall. Gold?"; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + CavesLevel.addVisuals( this, scene ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/CavesLevel.java b/src/com/watabou/pixeldungeon/levels/CavesLevel.java new file mode 100644 index 0000000000..0a1744910e --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/CavesLevel.java @@ -0,0 +1,284 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Scene; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.mobs.npcs.Blacksmith; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; +import com.watabou.utils.Rect; + +public class CavesLevel extends RegularLevel { + + { + color1 = 0x534f3e; + color2 = 0xb9d661; + + viewDistance = 6; + } + + @Override + public String tilesTex() { + return Assets.TILES_CAVES; + } + + @Override + public String waterTex() { + return Assets.WATER_CAVES; + } + + protected boolean[] water() { + return Patch.generate( feeling == Feeling.WATER ? 0.60f : 0.45f, 6 ); + } + + protected boolean[] grass() { + return Patch.generate( feeling == Feeling.GRASS ? 0.55f : 0.35f, 3 ); + } + + @Override + protected void assignRoomType() { + super.assignRoomType(); + + Blacksmith.Quest.spawn( rooms ); + } + + @Override + protected void decorate() { + + for (Room room : rooms) { + if (room.type != Room.Type.STANDARD) { + continue; + } + + if (room.width() <= 3 || room.height() <= 3) { + continue; + } + + int s = room.square(); + + if (Random.Int( s ) > 8) { + int corner = (room.left + 1) + (room.top + 1) * WIDTH; + if (map[corner - 1] == Terrain.WALL && map[corner - WIDTH] == Terrain.WALL) { + map[corner] = Terrain.WALL; + } + } + + if (Random.Int( s ) > 8) { + int corner = (room.right - 1) + (room.top + 1) * WIDTH; + if (map[corner + 1] == Terrain.WALL && map[corner - WIDTH] == Terrain.WALL) { + map[corner] = Terrain.WALL; + } + } + + if (Random.Int( s ) > 8) { + int corner = (room.left + 1) + (room.bottom - 1) * WIDTH; + if (map[corner - 1] == Terrain.WALL && map[corner + WIDTH] == Terrain.WALL) { + map[corner] = Terrain.WALL; + } + } + + if (Random.Int( s ) > 8) { + int corner = (room.right - 1) + (room.bottom - 1) * WIDTH; + if (map[corner + 1] == Terrain.WALL && map[corner + WIDTH] == Terrain.WALL) { + map[corner] = Terrain.WALL; + } + } + + for (Room n : room.connected.keySet()) { + if ((n.type == Room.Type.STANDARD || n.type == Room.Type.TUNNEL) && Random.Int( 3 ) == 0) { + Painter.set( this, room.connected.get( n ), Terrain.EMPTY_DECO ); + } + } + } + + for (int i=WIDTH + 1; i < LENGTH - WIDTH; i++) { + if (map[i] == Terrain.EMPTY) { + int n = 0; + if (map[i+1] == Terrain.WALL) { + n++; + } + if (map[i-1] == Terrain.WALL) { + n++; + } + if (map[i+WIDTH] == Terrain.WALL) { + n++; + } + if (map[i-WIDTH] == Terrain.WALL) { + n++; + } + if (Random.Int( 6 ) <= n) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.WALL && Random.Int( 12 ) == 0) { + map[i] = Terrain.WALL_DECO; + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + + if (Dungeon.bossLevel( Dungeon.depth + 1 )) { + return; + } + + for (Room r : rooms) { + if (r.type == Type.STANDARD) { + for (Room n : r.neigbours) { + if (n.type == Type.STANDARD && !r.connected.containsKey( n )/* && Random.Int( 2 ) == 0*/) { + Rect w = r.intersect( n ); + if (w.left == w.right && w.bottom - w.top >= 5) { + + w.top += 2; + w.bottom -= 1; + + w.right++; + + Painter.fill( this, w.left, w.top, 1, w.height(), Terrain.CHASM ); + + } else if (w.top == w.bottom && w.right - w.left >= 5) { + + w.left += 2; + w.right -= 1; + + w.bottom++; + + Painter.fill( this, w.left, w.top, w.width(), 1, Terrain.CHASM ); + } + } + } + } + } + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.GRASS: + return "Fluorescent moss"; + case Terrain.HIGH_GRASS: + return "Fluorescent mushrooms"; + case Terrain.WATER: + return "Freezing cold water."; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc( int tile ) { + switch (tile) { + case Terrain.ENTRANCE: + return "The ladder leads up to the upper depth."; + case Terrain.EXIT: + return "The ladder leads down to the lower depth."; + case Terrain.HIGH_GRASS: + return "Huge mushrooms block the view."; + case Terrain.WALL_DECO: + return "A vein of some ore is visible on the wall. Gold?"; + case Terrain.BOOKSHELF: + return "Who would need a bookshelf in a cave?"; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + super.addVisuals( scene ); + addVisuals( this, scene ); + } + + public static void addVisuals( Level level, Scene scene ) { + for (int i=0; i < LENGTH; i++) { + if (level.map[i] == Terrain.WALL_DECO) { + scene.add( new Vein( i ) ); + } + } + } + + private static class Vein extends Group { + + private int pos; + + private float delay; + + public Vein( int pos ) { + super(); + + this.pos = pos; + + delay = Random.Float( 2 ); + } + + @Override + public void update() { + + if (visible = Dungeon.visible[pos]) { + + super.update(); + + if ((delay -= Game.elapsed) <= 0) { + + delay = Random.Float(); + + PointF p = DungeonTilemap.tileToWorld( pos ); + ((Sparkle)recycle( Sparkle.class )).reset( + p.x + Random.Float( DungeonTilemap.SIZE ), + p.y + Random.Float( DungeonTilemap.SIZE ) ); + } + } + } + } + + public static final class Sparkle extends PixelParticle { + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan = 0.5f; + } + + @Override + public void update() { + super.update(); + + float p = left / lifespan; + size( (am = p < 0.5f ? p * 2 : (1 - p) * 2) * 2 ); + } + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/levels/CityBossLevel.java b/src/com/watabou/pixeldungeon/levels/CityBossLevel.java new file mode 100644 index 0000000000..8278cfc694 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/CityBossLevel.java @@ -0,0 +1,253 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Scene; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class CityBossLevel extends Level { + + { + color1 = 0x4b6636; + color2 = 0xf2f2f2; + } + + private static final int TOP = 2; + private static final int HALL_WIDTH = 7; + private static final int HALL_HEIGHT = 15; + private static final int CHAMBER_HEIGHT = 3; + + private static final int LEFT = (WIDTH - HALL_WIDTH) / 2; + private static final int CENTER = LEFT + HALL_WIDTH / 2; + + private int arenaDoor; + private boolean enteredArena = false; + private boolean keyDropped = false; + + @Override + public String tilesTex() { + return Assets.TILES_CITY; + } + + @Override + public String waterTex() { + return Assets.WATER_CITY; + } + + private static final String DOOR = "door"; + private static final String ENTERED = "entered"; + private static final String DROPPED = "droppped"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( DOOR, arenaDoor ); + bundle.put( ENTERED, enteredArena ); + bundle.put( DROPPED, keyDropped ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + arenaDoor = bundle.getInt( DOOR ); + enteredArena = bundle.getBoolean( ENTERED ); + keyDropped = bundle.getBoolean( DROPPED ); + } + + @Override + protected boolean build() { + + Painter.fill( this, LEFT, TOP, HALL_WIDTH, HALL_HEIGHT, Terrain.EMPTY ); + Painter.fill( this, CENTER, TOP, 1, HALL_HEIGHT, Terrain.EMPTY_SP ); + + int y = TOP + 1; + while (y < TOP + HALL_HEIGHT) { + map[y * WIDTH + CENTER - 2] = Terrain.STATUE_SP; + map[y * WIDTH + CENTER + 2] = Terrain.STATUE_SP; + y += 2; + } + + int left = pedestal( true ); + int right = pedestal( false ); + map[left] = map[right] = Terrain.PEDESTAL; + for (int i=left+1; i < right; i++) { + map[i] = Terrain.EMPTY_SP; + } + + exit = (TOP - 1) * WIDTH + CENTER; + map[exit] = Terrain.LOCKED_EXIT; + + arenaDoor = (TOP + HALL_HEIGHT) * WIDTH + CENTER; + map[arenaDoor] = Terrain.DOOR; + + Painter.fill( this, LEFT, TOP + HALL_HEIGHT + 1, HALL_WIDTH, CHAMBER_HEIGHT, Terrain.EMPTY ); + Painter.fill( this, LEFT, TOP + HALL_HEIGHT + 1, 1, CHAMBER_HEIGHT, Terrain.BOOKSHELF ); + Painter.fill( this, LEFT + HALL_WIDTH - 1, TOP + HALL_HEIGHT + 1, 1, CHAMBER_HEIGHT, Terrain.BOOKSHELF ); + + entrance = (TOP + HALL_HEIGHT + 2 + Random.Int( CHAMBER_HEIGHT - 1 )) * WIDTH + LEFT + (/*1 +*/ Random.Int( HALL_WIDTH-2 )); + map[entrance] = Terrain.ENTRANCE; + + return true; + } + + @Override + protected void decorate() { + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 10 ) == 0) { + map[i] = Terrain.EMPTY_DECO; + } else if (map[i] == Terrain.WALL && Random.Int( 8 ) == 0) { + map[i] = Terrain.WALL_DECO; + } + } + + int sign = arenaDoor + WIDTH + 1; + map[sign] = Terrain.SIGN; + } + + public static int pedestal( boolean left ) { + if (left) { + return (TOP + HALL_HEIGHT / 2) * WIDTH + CENTER - 2; + } else { + return (TOP + HALL_HEIGHT / 2) * WIDTH + CENTER + 2; + } + } + + @Override + protected void createMobs() { + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = + Random.IntRange( LEFT + 1, LEFT + HALL_WIDTH - 2 ) + + Random.IntRange( TOP + HALL_HEIGHT + 1, TOP + HALL_HEIGHT + CHAMBER_HEIGHT ) * WIDTH; + } while (pos == entrance || map[pos] == Terrain.SIGN); + drop( item, pos ).type = Heap.Type.SKELETON; + } + } + + @Override + public int randomRespawnCell() { + return -1; + } + + @Override + public void press( int cell, Char hero ) { + + super.press( cell, hero ); + + if (!enteredArena && outsideEntraceRoom( cell ) && hero == Dungeon.hero) { + + enteredArena = true; + + Mob boss = Bestiary.mob( Dungeon.depth ); + boss.state = Mob.State.HUNTING; + do { + boss.pos = Random.Int( LENGTH ); + } while ( + !passable[boss.pos] || + !outsideEntraceRoom( boss.pos ) || + Dungeon.visible[boss.pos]); + GameScene.add( boss ); + + set( arenaDoor, Terrain.LOCKED_DOOR ); + GameScene.updateMap( arenaDoor ); + Dungeon.observe(); + } + } + + @Override + public Heap drop( Item item, int cell ) { + + if (!keyDropped && item instanceof SkeletonKey) { + + keyDropped = true; + + set( arenaDoor, Terrain.DOOR ); + GameScene.updateMap( arenaDoor ); + Dungeon.observe(); + } + + return super.drop( item, cell ); + } + + private boolean outsideEntraceRoom( int cell ) { + return cell / WIDTH < arenaDoor / WIDTH; + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Suspiciously colored water"; + case Terrain.HIGH_GRASS: + return "High blooming flowers"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.ENTRANCE: + return "A ramp leads up to the upper depth."; + case Terrain.EXIT: + return "A ramp leads down to the lower depth."; + case Terrain.WALL_DECO: + case Terrain.EMPTY_DECO: + return "Several tiles are missing here."; + case Terrain.EMPTY_SP: + return "Thick carpet covers the floor."; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "The statue depicts some dwarf standing in a heroic stance."; + case Terrain.BOOKSHELF: + return "The rows of books on different disciplines fill the bookshelf."; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + CityLevel.addVisuals( this, scene ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/CityLevel.java b/src/com/watabou/pixeldungeon/levels/CityLevel.java new file mode 100644 index 0000000000..38778a43cd --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/CityLevel.java @@ -0,0 +1,200 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Scene; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class CityLevel extends RegularLevel { + + { + color1 = 0x4b6636; + color2 = 0xf2f2f2; + } + + @Override + public String tilesTex() { + return Assets.TILES_CITY; + } + + @Override + public String waterTex() { + return Assets.WATER_CITY; + } + + protected boolean[] water() { + return Patch.generate( feeling == Feeling.WATER ? 0.65f : 0.45f, 4 ); + } + + protected boolean[] grass() { + return Patch.generate( feeling == Feeling.GRASS ? 0.60f : 0.40f, 3 ); + } + + @Override + protected void assignRoomType() { + super.assignRoomType(); + + for (Room r : rooms) { + if (r.type == Type.TUNNEL) { + r.type = Type.PASSAGE; + } + } + } + + @Override + protected void decorate() { + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 10 ) == 0) { + map[i] = Terrain.EMPTY_DECO; + } else if (map[i] == Terrain.WALL && Random.Int( 8 ) == 0) { + map[i] = Terrain.WALL_DECO; + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + } + + @Override + protected void createItems() { + super.createItems(); + + Imp.Quest.spawn( this, roomEntrance ); + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Suspiciously colored water"; + case Terrain.HIGH_GRASS: + return "High blooming flowers"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.ENTRANCE: + return "A ramp leads up to the upper depth."; + case Terrain.EXIT: + return "A ramp leads down to the lower depth."; + case Terrain.WALL_DECO: + case Terrain.EMPTY_DECO: + return "Several tiles are missing here."; + case Terrain.EMPTY_SP: + return "Thick carpet covers the floor."; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "The statue depicts some dwarf standing in a heroic stance."; + case Terrain.BOOKSHELF: + return "The rows of books on different disciplines fill the bookshelf."; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + super.addVisuals( scene ); + addVisuals( this, scene ); + } + + public static void addVisuals( Level level, Scene scene ) { + for (int i=0; i < LENGTH; i++) { + if (level.map[i] == Terrain.WALL_DECO) { + scene.add( new Smoke( i ) ); + } + } + } + + private static class Smoke extends Emitter { + + private int pos; + + private static final Emitter.Factory factory = new Factory() { + + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + SmokeParticle p = (SmokeParticle)emitter.recycle( SmokeParticle.class ); + p.reset( x, y ); + } + }; + + public Smoke( int pos ) { + super(); + + this.pos = pos; + + PointF p = DungeonTilemap.tileCenterToWorld( pos ); + pos( p.x - 4, p.y - 2, 4, 0 ); + + pour( factory, 0.2f ); + } + + @Override + public void update() { + if (visible = Dungeon.visible[pos]) { + super.update(); + } + } + } + + public static final class SmokeParticle extends PixelParticle { + + public SmokeParticle() { + super(); + + color( 0x000000 ); + speed.set( Random.Float( 8 ), -Random.Float( 8 ) ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan = 2f; + } + + @Override + public void update() { + super.update(); + float p = left / lifespan; + am = p > 0.8f ? 1 - p : p * 0.25f; + size( 8 - p * 4 ); + } + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/levels/DeadEndLevel.java b/src/com/watabou/pixeldungeon/levels/DeadEndLevel.java new file mode 100644 index 0000000000..5d87f4b29d --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/DeadEndLevel.java @@ -0,0 +1,97 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.Arrays; + +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.Random; + +public class DeadEndLevel extends Level { + + private static final int SIZE = 5; + + { + color1 = 0x534f3e; + color2 = 0xb9d661; + } + + @Override + public String tilesTex() { + return Assets.TILES_CAVES; + } + + @Override + public String waterTex() { + return Assets.WATER_HALLS; + } + + @Override + protected boolean build() { + + Arrays.fill( map, Terrain.WALL ); + + for (int i=2; i < SIZE; i++) { + for (int j=2; j < SIZE; j++) { + map[i * WIDTH + j] = Terrain.EMPTY; + } + } + + for (int i=1; i <= SIZE; i++) { + map[WIDTH + i] = + map[WIDTH * SIZE + i] = + map[WIDTH * i + 1] = + map[WIDTH * i + SIZE] = + Terrain.WATER; + } + + entrance = SIZE * WIDTH + SIZE / 2 + 1; + map[entrance] = Terrain.ENTRANCE; + + exit = -1; + + map[(SIZE / 2 + 1) * (WIDTH + 1)] = Terrain.SIGN; + + return true; + } + + @Override + protected void decorate() { + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 10 ) == 0) { + map[i] = Terrain.EMPTY_DECO; + } else if (map[i] == Terrain.WALL && Random.Int( 8 ) == 0) { + map[i] = Terrain.WALL_DECO; + } + } + } + + @Override + protected void createMobs() { + } + + @Override + protected void createItems() { + } + + @Override + public int randomRespawnCell() { + return -1; + } + +} diff --git a/src/com/watabou/pixeldungeon/levels/HallsBossLevel.java b/src/com/watabou/pixeldungeon/levels/HallsBossLevel.java new file mode 100644 index 0000000000..1c22aea583 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/HallsBossLevel.java @@ -0,0 +1,253 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Scene; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Yog; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class HallsBossLevel extends Level { + + { + color1 = 0x801500; + color2 = 0xa68521; + + viewDistance = 3; + } + + private static final int ROOM_LEFT = WIDTH / 2 - 1; + private static final int ROOM_RIGHT = WIDTH / 2 + 1; + private static final int ROOM_TOP = HEIGHT / 2 - 1; + private static final int ROOM_BOTTOM = HEIGHT / 2 + 1; + + private int stairs = -1; + private boolean enteredArena = false; + private boolean keyDropped = false; + + @Override + public String tilesTex() { + return Assets.TILES_HALLS; + } + + @Override + public String waterTex() { + return Assets.WATER_HALLS; + } + + private static final String STAIRS = "stairs"; + private static final String ENTERED = "entered"; + private static final String DROPPED = "droppped"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( STAIRS, stairs ); + bundle.put( ENTERED, enteredArena ); + bundle.put( DROPPED, keyDropped ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + stairs = bundle.getInt( STAIRS ); + enteredArena = bundle.getBoolean( ENTERED ); + keyDropped = bundle.getBoolean( DROPPED ); + } + + @Override + protected boolean build() { + + for (int i=0; i < 5; i++) { + + int top = Random.IntRange( 2, ROOM_TOP - 1 ); + int bottom = Random.IntRange( ROOM_BOTTOM + 1, 22 ); + Painter.fill( this, 2 + i * 4, top, 4, bottom - top + 1, Terrain.EMPTY ); + + if (i == 2) { + exit = (i * 4 + 3) + (top - 1) * WIDTH ; + } + + for (int j=0; j < 4; j++) { + if (Random.Int( 2 ) == 0) { + int y = Random.IntRange( top + 1, bottom - 1 ); + map[i*4+j + y*WIDTH] = Terrain.WALL_DECO; + } + } + } + + map[exit] = Terrain.LOCKED_EXIT; + + Painter.fill( this, ROOM_LEFT - 1, ROOM_TOP - 1, + ROOM_RIGHT - ROOM_LEFT + 3, ROOM_BOTTOM - ROOM_TOP + 3, Terrain.WALL ); + Painter.fill( this, ROOM_LEFT, ROOM_TOP, + ROOM_RIGHT - ROOM_LEFT + 1, ROOM_BOTTOM - ROOM_TOP + 1, Terrain.EMPTY ); + + entrance = Random.Int( ROOM_LEFT + 1, ROOM_RIGHT - 1 ) + + Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * WIDTH; + map[entrance] = Terrain.ENTRANCE; + + boolean[] patch = Patch.generate( 0.45f, 6 ); + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && patch[i]) { + map[i] = Terrain.WATER; + } + } + + return true; + } + + @Override + protected void decorate() { + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 10 ) == 0) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + @Override + protected void createMobs() { + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = Random.IntRange( ROOM_LEFT, ROOM_RIGHT ) + Random.IntRange( ROOM_TOP + 1, ROOM_BOTTOM ) * WIDTH; + } while (pos == entrance || map[pos] == Terrain.SIGN); + drop( item, pos ).type = Heap.Type.SKELETON; + } + } + + @Override + public int randomRespawnCell() { + return -1; + } + + @Override + public void press( int cell, Char hero ) { + + super.press( cell, hero ); + + if (!enteredArena && hero == Dungeon.hero && cell != entrance) { + + enteredArena = true; + + for (int i=ROOM_LEFT-1; i <= ROOM_RIGHT + 1; i++) { + doMagic( (ROOM_TOP - 1) * WIDTH + i ); + doMagic( (ROOM_BOTTOM + 1) * WIDTH + i ); + } + for (int i=ROOM_TOP; i < ROOM_BOTTOM + 1; i++) { + doMagic( i * WIDTH + ROOM_LEFT - 1 ); + doMagic( i * WIDTH + ROOM_RIGHT + 1 ); + } + doMagic( entrance ); + GameScene.updateMap(); + + Dungeon.observe(); + + Yog boss = new Yog(); + do { + boss.pos = Random.Int( LENGTH ); + } while ( + !passable[boss.pos] || + Dungeon.visible[boss.pos]); + GameScene.add( boss ); + boss.spawnFists(); + + stairs = entrance; + entrance = -1; + } + } + + private void doMagic( int cell ) { + set( cell, Terrain.EMPTY_SP ); + CellEmitter.get( cell ).start( FlameParticle.FACTORY, 0.1f, 3 ); + } + + @Override + public Heap drop( Item item, int cell ) { + + if (!keyDropped && item instanceof SkeletonKey) { + keyDropped = true; + + entrance = stairs; + set( entrance, Terrain.ENTRANCE ); + GameScene.updateMap( entrance ); + } + + return super.drop( item, cell ); + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Cold lava"; + case Terrain.GRASS: + return "Embermoss"; + case Terrain.HIGH_GRASS: + return "Emberfungi"; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "Pillar"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.WATER: + return "It looks like lava, but it's cold and probably safe to touch."; + case Terrain.EMPTY_DECO: + return "Candles on the floor are probably remains of some ritual that took place here."; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "The pillar is made of real humanoid skulls. Awesome."; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + HallsLevel.addVisuals( this, scene ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/HallsLevel.java b/src/com/watabou/pixeldungeon/levels/HallsLevel.java new file mode 100644 index 0000000000..9c02248dd0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/HallsLevel.java @@ -0,0 +1,232 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Scene; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.items.Torch; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class HallsLevel extends RegularLevel { + + { + minRoomSize = 6; + + viewDistance = Math.max( 25 - Dungeon.depth, 1 ); + + color1 = 0x801500; + color2 = 0xa68521; + } + + @Override + public void create() { + addItemToSpawn( new Torch() ); + super.create(); + } + + @Override + public String tilesTex() { + return Assets.TILES_HALLS; + } + + @Override + public String waterTex() { + return Assets.WATER_HALLS; + } + + protected boolean[] water() { + return Patch.generate( feeling == Feeling.WATER ? 0.55f : 0.40f, 6 ); + } + + protected boolean[] grass() { + return Patch.generate( feeling == Feeling.GRASS ? 0.55f : 0.30f, 3 ); + } + + @Override + protected void decorate() { + + for (int i=WIDTH + 1; i < LENGTH - WIDTH - 1; i++) { + if (map[i] == Terrain.EMPTY) { + + int count = 0; + for (int j=0; j < NEIGHBOURS8.length; j++) { + if ((Terrain.flags[map[i + NEIGHBOURS8[j]]] & Terrain.PASSABLE) > 0) { + count++; + } + } + + if (Random.Int( 80 ) < count) { + map[i] = Terrain.EMPTY_DECO; + } + + } else + if (map[i] == Terrain.WALL) { + + int count = 0; + for (int j=0; j < NEIGHBOURS4.length; j++) { + if (map[i + NEIGHBOURS4[j]] == Terrain.WATER) { + count++; + } + } + + if (Random.Int( 4 ) < count) { + map[i] = Terrain.WALL_DECO; + } + + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Cold lava"; + case Terrain.GRASS: + return "Embermoss"; + case Terrain.HIGH_GRASS: + return "Emberfungi"; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "Pillar"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.WATER: + return "It looks like lava, but it's cold and probably safe to touch."; + case Terrain.EMPTY_DECO: + return "Candles on the floor are probably remains of some ritual that took place here."; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "The pillar is made of real humanoid skulls. Awesome."; + case Terrain.BOOKSHELF: + return "Books in ancient languages smoulder in the bookshelf."; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + super.addVisuals( scene ); + addVisuals( this, scene ); + } + + public static void addVisuals( Level level, Scene scene ) { + for (int i=0; i < LENGTH; i++) { + if (level.map[i] == 63) { + scene.add( new Stream( i ) ); + } + } + } + + private static class Stream extends Group { + + private int pos; + + private float delay; + + public Stream( int pos ) { + super(); + + this.pos = pos; + + delay = Random.Float( 2 ); + } + + @Override + public void update() { + + if (visible = Dungeon.visible[pos]) { + + super.update(); + + if ((delay -= Game.elapsed) <= 0) { + + delay = Random.Float( 2 ); + + PointF p = DungeonTilemap.tileToWorld( pos ); + ((FireParticle)recycle( FireParticle.class )).reset( + p.x + Random.Float( DungeonTilemap.SIZE ), + p.y + Random.Float( DungeonTilemap.SIZE ) ); + } + } + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } + } + + public static class FireParticle extends PixelParticle.Shrinking { + + public FireParticle() { + super(); + + color( 0xEE7722 ); + lifespan = 1f; + + acc.set( 0, +80 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + + speed.set( 0, -40 ); + size = 4; + } + + @Override + public void update() { + super.update(); + float p = left / lifespan; + am = p > 0.8f ? (1 - p) * 5 : 1; + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/LastLevel.java b/src/com/watabou/pixeldungeon/levels/LastLevel.java new file mode 100644 index 0000000000..6df523ea75 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/LastLevel.java @@ -0,0 +1,131 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.Arrays; + +import com.watabou.noosa.Scene; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.items.Amulet; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.utils.Random; + +public class LastLevel extends Level { + + private static final int SIZE = 7; + + { + color1 = 0x801500; + color2 = 0xa68521; + } + + private int pedestal; + + @Override + public String tilesTex() { + return Assets.TILES_HALLS; + } + + @Override + public String waterTex() { + return Assets.WATER_HALLS; + } + + @Override + protected boolean build() { + + Arrays.fill( map, Terrain.WALL ); + Painter.fill( this, 1, 1, SIZE, SIZE, Terrain.WATER ); + Painter.fill( this, 2, 2, SIZE-2, SIZE-2, Terrain.EMPTY ); + Painter.fill( this, SIZE/2, SIZE/2, 3, 3, Terrain.EMPTY_SP ); + + entrance = SIZE * WIDTH + SIZE / 2 + 1; + map[entrance] = Terrain.ENTRANCE; + + exit = entrance - WIDTH * SIZE; + map[exit] = Terrain.LOCKED_EXIT; + + pedestal = (SIZE / 2 + 1) * (WIDTH + 1); + map[pedestal] = Terrain.PEDESTAL; + map[pedestal-1] = map[pedestal+1] = Terrain.STATUE_SP; + + feeling = Feeling.NONE; + + return true; + } + + @Override + protected void decorate() { + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 10 ) == 0) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + @Override + protected void createMobs() { + } + + @Override + protected void createItems() { + drop( new Amulet(), pedestal ); + } + + @Override + public int randomRespawnCell() { + return -1; + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Cold lava"; + case Terrain.GRASS: + return "Embermoss"; + case Terrain.HIGH_GRASS: + return "Emberfungi"; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "Pillar"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.WATER: + return "It looks like lava, but it's cold and probably safe to touch."; + case Terrain.EMPTY_DECO: + return "Candles on the floor are probably remains of some ritual that took place here."; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "The pillar is made of real humanoid skulls. Awesome."; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + HallsLevel.addVisuals( this, scene ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/LastShopLevel.java b/src/com/watabou/pixeldungeon/levels/LastShopLevel.java new file mode 100644 index 0000000000..981cbc1cb0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/LastShopLevel.java @@ -0,0 +1,225 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.List; + +import com.watabou.noosa.Scene; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.utils.Graph; +import com.watabou.utils.Random; + +public class LastShopLevel extends RegularLevel { + + { + color1 = 0x4b6636; + color2 = 0xf2f2f2; + } + + @Override + public String tilesTex() { + return Assets.TILES_CITY; + } + + @Override + public String waterTex() { + return Assets.WATER_CITY; + } + + @Override + protected boolean build() { + + initRooms(); + + int distance; + int retry = 0; + int minDistance = (int)Math.sqrt( rooms.size() ); + do { + int innerRetry = 0; + do { + if (innerRetry++ > 10) { + return false; + } + roomEntrance = Random.element( rooms ); + } while (roomEntrance.width() < 4 || roomEntrance.height() < 4); + + innerRetry = 0; + do { + if (innerRetry++ > 10) { + return false; + } + roomExit = Random.element( rooms ); + } while (roomExit == roomEntrance || roomExit.width() < 6 || roomExit.height() < 6 || roomExit.top == 0); + + Graph.buildDistanceMap( rooms, roomExit ); + distance = Graph.buildPath( rooms, roomEntrance, roomExit ).size(); + + if (retry++ > 10) { + return false; + } + + } while (distance < minDistance); + + roomEntrance.type = Type.ENTRANCE; + roomExit.type = Type.EXIT; + + Graph.buildDistanceMap( rooms, roomExit ); + List path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + Graph.setPrice( path, roomEntrance.distance ); + + Graph.buildDistanceMap( rooms, roomExit ); + path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + Room room = roomEntrance; + for (Room next : path) { + room.connect( next ); + room = next; + } + + Room roomShop = null; + int shopSquare = 0; + for (Room r : rooms) { + if (r.type == Type.NULL && r.connected.size() > 0) { + r.type = Type.PASSAGE; + if (r.square() > shopSquare) { + roomShop = r; + shopSquare = r.square(); + } + } + } + + if (roomShop == null || shopSquare < 30) { + return false; + } else { + roomShop.type = Imp.Quest.isCompleted() ? Room.Type.SHOP : Room.Type.STANDARD; + } + + paint(); + + paintWater(); + paintGrass(); + + return true; + } + + @Override + protected void decorate() { + + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && Random.Int( 10 ) == 0) { + + map[i] = Terrain.EMPTY_DECO; + + } else if (map[i] == Terrain.WALL && Random.Int( 8 ) == 0) { + + map[i] = Terrain.WALL_DECO; + + } else if (map[i] == Terrain.SECRET_DOOR) { + + map[i] = Terrain.DOOR; + + } + } + + if (Imp.Quest.isCompleted()) { + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + } + } + + @Override + protected void createMobs() { + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = roomEntrance.random(); + } while (pos == entrance || map[pos] == Terrain.SIGN); + drop( item, pos ).type = Heap.Type.SKELETON; + } + } + + @Override + public int randomRespawnCell() { + return -1; + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Suspiciously colored water"; + case Terrain.HIGH_GRASS: + return "High blooming flowers"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.ENTRANCE: + return "A ramp leads up to the upper depth."; + case Terrain.EXIT: + return "A ramp leads down to the Inferno."; + case Terrain.WALL_DECO: + case Terrain.EMPTY_DECO: + return "Several tiles are missing here."; + case Terrain.EMPTY_SP: + return "Thick carpet covers the floor."; + default: + return super.tileDesc( tile ); + } + } + + @Override + protected boolean[] water() { + return Patch.generate( 0.35f, 4 ); + } + + @Override + protected boolean[] grass() { + return Patch.generate( 0.30f, 3 ); + } + + @Override + public void addVisuals( Scene scene ) { + CityLevel.addVisuals( this, scene ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/Level.java b/src/com/watabou/pixeldungeon/levels/Level.java new file mode 100644 index 0000000000..4aa1ff729c --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/Level.java @@ -0,0 +1,973 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; + +import com.watabou.noosa.Scene; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Alchemy; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.WellWater; +import com.watabou.pixeldungeon.actors.buffs.Awareness; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.MindVision; +import com.watabou.pixeldungeon.actors.buffs.Shadows; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.particles.FlowParticle; +import com.watabou.pixeldungeon.effects.particles.WindParticle; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Stylus; +import com.watabou.pixeldungeon.items.potions.PotionOfStrength; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.levels.features.Chasm; +import com.watabou.pixeldungeon.levels.features.Door; +import com.watabou.pixeldungeon.levels.features.HighGrass; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.pixeldungeon.levels.traps.*; +import com.watabou.pixeldungeon.mechanics.ShadowCaster; +import com.watabou.pixeldungeon.plants.Plant; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; +import com.watabou.utils.SparseArray; + +public abstract class Level implements Bundlable { + + public static enum Feeling { + NONE, + CHASM, + WATER, + GRASS + }; + + public static final int WIDTH = 32; + public static final int HEIGHT = 32; + public static final int LENGTH = WIDTH * HEIGHT; + + public static final int[] NEIGHBOURS4 = {-WIDTH, +1, +WIDTH, -1}; + public static final int[] NEIGHBOURS8 = {+1, -1, +WIDTH, -WIDTH, +1+WIDTH, +1-WIDTH, -1+WIDTH, -1-WIDTH}; + public static final int[] NEIGHBOURS9 = {0, +1, -1, +WIDTH, -WIDTH, +1+WIDTH, +1-WIDTH, -1+WIDTH, -1-WIDTH}; + + protected static final float TIME_TO_RESPAWN = 50; + + private static final String TXT_HIDDEN_PLATE_CLICKS = "A hidden pressure plate clicks!"; + + public static boolean resizingNeeded; + // This one can be different from resizingNeeded if the level + // was created in the older version of the game + public static int loadedMapSize; + + public int[] map; + public boolean[] visited; + public boolean[] mapped; + + public int viewDistance = 8; + + public static boolean[] fieldOfView = new boolean[LENGTH]; + + public static boolean[] passable = new boolean[LENGTH]; + public static boolean[] losBlocking = new boolean[LENGTH]; + public static boolean[] flamable = new boolean[LENGTH]; + public static boolean[] secret = new boolean[LENGTH]; + public static boolean[] solid = new boolean[LENGTH]; + public static boolean[] avoid = new boolean[LENGTH]; + public static boolean[] water = new boolean[LENGTH]; + public static boolean[] pit = new boolean[LENGTH]; + + public static boolean[] discoverable = new boolean[LENGTH]; + + public Feeling feeling = Feeling.NONE; + + public int entrance; + public int exit; + + public HashSet mobs; + public SparseArray heaps; + public HashMap,Blob> blobs; + public SparseArray plants; + + protected ArrayList itemsToSpawn = new ArrayList(); + + public int color1 = 0x004400; + public int color2 = 0x88CC44; + + protected static boolean pitRoomNeeded = false; + protected static boolean weakFloorCreated = false; + + private static final String MAP = "map"; + private static final String VISITED = "visited"; + private static final String MAPPED = "mapped"; + private static final String ENTRANCE = "entrance"; + private static final String EXIT = "exit"; + private static final String HEAPS = "heaps"; + private static final String PLANTS = "plants"; + private static final String MOBS = "mobs"; + private static final String BLOBS = "blobs"; + + public void create() { + + resizingNeeded = false; + + map = new int[LENGTH]; + visited = new boolean[LENGTH]; + Arrays.fill( visited, false ); + mapped = new boolean[LENGTH]; + Arrays.fill( mapped, false ); + + mobs = new HashSet(); + heaps = new SparseArray(); + blobs = new HashMap,Blob>(); + plants = new SparseArray(); + + if (!Dungeon.bossLevel()) { + addItemToSpawn( Generator.random( Generator.Category.FOOD ) ); + if (Dungeon.posNeeded()) { + addItemToSpawn( new PotionOfStrength() ); + Dungeon.potionOfStrength++; + } + if (Dungeon.soeNeeded()) { + addItemToSpawn( new ScrollOfUpgrade() ); + Dungeon.scrollsOfUpgrade++; + } + if (Dungeon.asNeeded()) { + addItemToSpawn( new Stylus() ); + Dungeon.arcaneStyli++; + } + + if (Dungeon.depth > 1) { + switch (Random.Int( 10 )) { + case 0: + if (!Dungeon.bossLevel( Dungeon.depth + 1 )) { + feeling = Feeling.CHASM; + } + break; + case 1: + feeling = Feeling.WATER; + break; + case 2: + feeling = Feeling.GRASS; + break; + } + } + } + + boolean pitNeeded = Dungeon.depth == 1 ? false : weakFloorCreated; + + do { + Arrays.fill( map, feeling == Feeling.CHASM ? Terrain.CHASM : Terrain.WALL ); + + pitRoomNeeded = pitNeeded; + weakFloorCreated = false; + + } while (!build()); + decorate(); + + buildFlagMaps(); + cleanWalls(); + + createMobs(); + createItems(); + } + + public void reset() { + + for (Mob mob : mobs.toArray( new Mob[0] )) { + if (!mob.reset()) { + mobs.remove( mob ); + } + } + createMobs(); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + + mobs = new HashSet(); + heaps = new SparseArray(); + blobs = new HashMap, Blob>(); + plants = new SparseArray(); + + map = bundle.getIntArray( MAP ); + visited = bundle.getBooleanArray( VISITED ); + mapped = bundle.getBooleanArray( MAPPED ); + + entrance = bundle.getInt( ENTRANCE ); + exit = bundle.getInt( EXIT ); + + adjustMapSize(); + + Collection collection = bundle.getCollection( HEAPS ); + for (Bundlable h : collection) { + Heap heap = (Heap)h; + if (resizingNeeded) { + heap.pos = adjustPos( heap.pos ); + } + heaps.put( heap.pos, heap ); + } + + collection = bundle.getCollection( PLANTS ); + for (Bundlable p : collection) { + Plant plant = (Plant)p; + if (resizingNeeded) { + plant.pos = adjustPos( plant.pos ); + } + plants.put( plant.pos, plant ); + } + + collection = bundle.getCollection( MOBS ); + for (Bundlable m : collection) { + Mob mob = (Mob)m; + if (mob != null) { + if (resizingNeeded) { + mob.pos = adjustPos( mob.pos ); + } + mobs.add( mob ); + } + } + + collection = bundle.getCollection( BLOBS ); + for (Bundlable b : collection) { + Blob blob = (Blob)b; + blobs.put( blob.getClass(), blob ); + } + + buildFlagMaps(); + cleanWalls(); + } + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( MAP, map ); + bundle.put( VISITED, visited ); + bundle.put( MAPPED, mapped ); + bundle.put( ENTRANCE, entrance ); + bundle.put( EXIT, exit ); + bundle.put( HEAPS, heaps.values() ); + bundle.put( PLANTS, plants.values() ); + bundle.put( MOBS, mobs ); + bundle.put( BLOBS, blobs.values() ); + } + + public int tunnelTile() { + return feeling == Feeling.CHASM ? Terrain.EMPTY_SP : Terrain.EMPTY; + } + + private void adjustMapSize() { + // For levels from older saves + if (map.length < LENGTH) { + + resizingNeeded = true; + loadedMapSize = (int)Math.sqrt( map.length ); + + int[] map = new int[LENGTH]; + Arrays.fill( map, Terrain.WALL ); + + boolean[] visited = new boolean[LENGTH]; + Arrays.fill( visited, false ); + + boolean[] mapped = new boolean[LENGTH]; + Arrays.fill( mapped, false ); + + for (int i=0; i < loadedMapSize; i++) { + System.arraycopy( this.map, i * loadedMapSize, map, i * WIDTH, loadedMapSize ); + System.arraycopy( this.visited, i * loadedMapSize, visited, i * WIDTH, loadedMapSize ); + System.arraycopy( this.mapped, i * loadedMapSize, mapped, i * WIDTH, loadedMapSize ); + } + + this.map = map; + this.visited = visited; + this.mapped = mapped; + + entrance = adjustPos( entrance ); + exit = adjustPos( exit ); + } else { + resizingNeeded = false; + } + } + + public int adjustPos( int pos ) { + return (pos / loadedMapSize) * WIDTH + (pos % loadedMapSize); + } + + public String tilesTex() { + return null; + } + + public String waterTex() { + return null; + } + + abstract protected boolean build(); + abstract protected void decorate(); + abstract protected void createMobs(); + abstract protected void createItems(); + + public void addVisuals( Scene scene ) { + for (int i=0; i < LENGTH; i++) { + if (pit[i]) { + scene.add( new WindParticle.Wind( i ) ); + if (i >= WIDTH && water[i-WIDTH]) { + scene.add( new FlowParticle.Flow( i - WIDTH ) ); + } + } + } + } + + public int nMobs() { + return 0; + } + + public Actor respawner() { + return new Actor() { + @Override + protected boolean act() { + if (mobs.size() < nMobs()) { + + Mob mob = Bestiary.mutable( Dungeon.depth ); + mob.state = Mob.State.WANDERING; + mob.pos = randomRespawnCell(); + if (Dungeon.hero.isAlive() && mob.pos != -1) { + GameScene.add( mob ); + if (Statistics.amuletObtained) { + mob.beckon( Dungeon.hero.pos ); + } + } + } + spend( Dungeon.nightMode || Statistics.amuletObtained ? TIME_TO_RESPAWN / 2 : TIME_TO_RESPAWN ); + return true; + } + }; + } + + public int randomRespawnCell() { + int cell; + do { + cell = Random.Int( LENGTH ); + } while (!passable[cell] || Dungeon.visible[cell] || Actor.findChar( cell ) != null); + return cell; + } + + public int randomDestination() { + int cell; + do { + cell = Random.Int( LENGTH ); + } while (!passable[cell]); + return cell; + } + + public void addItemToSpawn( Item item ) { + if (item != null) { + itemsToSpawn.add( item ); + } + } + + public Item itemToSpanAsPrize() { + if (Random.Int( itemsToSpawn.size() + 1 ) > 0) { + Item item = Random.element( itemsToSpawn ); + itemsToSpawn.remove( item ); + return item; + } else { + return null; + } + } + + private void buildFlagMaps() { + + for (int i=0; i < LENGTH; i++) { + int flags = Terrain.flags[map[i]]; + passable[i] = (flags & Terrain.PASSABLE) != 0; + losBlocking[i] = (flags & Terrain.LOS_BLOCKING) != 0; + flamable[i] = (flags & Terrain.FLAMABLE) != 0; + secret[i] = (flags & Terrain.SECRET) != 0; + solid[i] = (flags & Terrain.SOLID) != 0; + avoid[i] = (flags & Terrain.AVOID) != 0; + water[i] = (flags & Terrain.LIQUID) != 0; + pit[i] = (flags & Terrain.PIT) != 0; + } + + int lastRow = LENGTH - WIDTH; + for (int i=0; i < WIDTH; i++) { + passable[i] = avoid[i] = false; + passable[lastRow + i] = avoid[lastRow + i] = false; + } + for (int i=WIDTH; i < lastRow; i += WIDTH) { + passable[i] = avoid[i] = false; + passable[i + WIDTH-1] = avoid[i + WIDTH-1] = false; + } + + for (int i=WIDTH; i < LENGTH - WIDTH; i++) { + + if (water[i]) { + int t = Terrain.WATER_TILES; + for (int j=0; j < NEIGHBOURS4.length; j++) { + if ((Terrain.flags[map[i + NEIGHBOURS4[j]]] & Terrain.UNSTITCHABLE) != 0) { + t += 1 << j; + } + } + map[i] = t; + } + + if (pit[i]) { + if (!pit[i - WIDTH]) { + int c = map[i - WIDTH]; + if (c == Terrain.EMPTY_SP || c == Terrain.STATUE_SP) { + map[i] = Terrain.CHASM_FLOOR_SP; + } else if (water[i - WIDTH]) { + map[i] = Terrain.CHASM_WATER; + } else if ((Terrain.flags[c] & Terrain.UNSTITCHABLE) != 0) { + map[i] = Terrain.CHASM_WALL; + } else { + map[i] = Terrain.CHASM_FLOOR; + } + } + } + } + } + + private void cleanWalls() { + for (int i=0; i < LENGTH; i++) { + + boolean d = false; + + for (int j=0; j < NEIGHBOURS9.length; j++) { + int n = i + NEIGHBOURS9[j]; + if (n >= 0 && n < LENGTH && map[n] != Terrain.WALL && map[n] != Terrain.WALL_DECO) { + d = true; + break; + } + } + + if (d) { + d = false; + + for (int j=0; j < NEIGHBOURS9.length; j++) { + int n = i + NEIGHBOURS9[j]; + if (n >= 0 && n < LENGTH && !pit[n]) { + d = true; + break; + } + } + } + + discoverable[i] = d; + } + } + + public static void set( int cell, int terrain ) { + Painter.set( Dungeon.level, cell, terrain ); + + int flags = Terrain.flags[terrain]; + passable[cell] = (flags & Terrain.PASSABLE) != 0; + losBlocking[cell] = (flags & Terrain.LOS_BLOCKING) != 0; + flamable[cell] = (flags & Terrain.FLAMABLE) != 0; + secret[cell] = (flags & Terrain.SECRET) != 0; + solid[cell] = (flags & Terrain.SOLID) != 0; + avoid[cell] = (flags & Terrain.AVOID) != 0; + pit[cell] = (flags & Terrain.PIT) != 0; + water[cell] = terrain == Terrain.WATER || terrain >= Terrain.WATER_TILES; + } + + public Heap drop( Item item, int cell ) { + + if ((map[cell] == Terrain.ALCHEMY) && !(item instanceof Plant.Seed)) { + int n; + do { + n = cell + NEIGHBOURS8[Random.Int( 8 )]; + } while (map[n] != Terrain.EMPTY_SP); + cell = n; + } + + Heap heap = heaps.get( cell ); + if (heap == null) { + + heap = new Heap(); + heap.pos = cell; + if (map[cell] == Terrain.CHASM || (Dungeon.level != null && pit[cell])) { + GameScene.discard( heap ); + } else { + heaps.put( cell, heap ); + GameScene.add( heap ); + } + + } else if (heap.type == Heap.Type.LOCKED_CHEST || heap.type == Heap.Type.CRYSTAL_CHEST) { + + int n; + do { + n = cell + Level.NEIGHBOURS8[Random.Int( 8 )]; + } while (!Level.passable[n] && !Level.avoid[n]); + return drop( item, n ); + + } + heap.drop( item ); + + if (Dungeon.level != null) { + press( cell, null ); + } + + return heap; + } + + public Plant plant( Plant.Seed seed, int pos ) { + + Plant plant = plants.get( pos ); + if (plant != null) { + plant.wither(); + } + + plant = seed.couch( pos ); + plants.put( pos, plant ); + + GameScene.add( plant ); + + return plant; + } + + public void uproot( int pos ) { + plants.delete( pos ); + } + + public int pitCell() { + return randomRespawnCell(); + } + + public void press( int cell, Char ch ) { + + if (pit[cell] && ch == Dungeon.hero) { + Chasm.heroFall( cell ); + return; + } + + boolean trap = false; + + switch (map[cell]) { + + case Terrain.SECRET_TOXIC_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.TOXIC_TRAP: + trap = true; + ToxicTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_FIRE_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.FIRE_TRAP: + trap = true; + FireTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_PARALYTIC_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.PARALYTIC_TRAP: + trap = true; + ParalyticTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_POISON_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.POISON_TRAP: + trap = true; + PoisonTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_ALARM_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.ALARM_TRAP: + trap = true; + AlarmTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_LIGHTNING_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.LIGHTNING_TRAP: + trap = true; + LightningTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_GRIPPING_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.GRIPPING_TRAP: + trap = true; + GrippingTrap.trigger( cell, ch ); + break; + + case Terrain.SECRET_SUMMONING_TRAP: + GLog.i( TXT_HIDDEN_PLATE_CLICKS ); + case Terrain.SUMMONING_TRAP: + trap = true; + SummoningTrap.trigger( cell, ch ); + break; + + case Terrain.HIGH_GRASS: + HighGrass.trample( this, cell, ch ); + break; + + case Terrain.WELL: + WellWater.affectCell( cell ); + break; + + case Terrain.ALCHEMY: + if (ch == null) { + Alchemy.transmute( cell ); + } + break; + + case Terrain.DOOR: + Door.enter( cell ); + break; + } + + if (trap) { + Sample.INSTANCE.play( Assets.SND_TRAP ); + if (ch == Dungeon.hero) { + Dungeon.hero.interrupt(); + } + set( cell, Terrain.INACTIVE_TRAP ); + GameScene.updateMap( cell ); + } + + Plant plant = plants.get( cell ); + if (plant != null) { + plant.activate( ch ); + } + } + + public void mobPress( Mob mob ) { + + int cell = mob.pos; + + if (pit[cell] && !mob.flying) { + Chasm.mobFall( mob ); + return; + } + + boolean trap = true; + switch (map[cell]) { + + case Terrain.TOXIC_TRAP: + ToxicTrap.trigger( cell, mob ); + break; + + case Terrain.FIRE_TRAP: + FireTrap.trigger( cell, mob ); + break; + + case Terrain.PARALYTIC_TRAP: + ParalyticTrap.trigger( cell, mob ); + break; + + case Terrain.POISON_TRAP: + PoisonTrap.trigger( cell, mob ); + break; + + case Terrain.ALARM_TRAP: + AlarmTrap.trigger( cell, mob ); + break; + + case Terrain.LIGHTNING_TRAP: + LightningTrap.trigger( cell, mob ); + break; + + case Terrain.GRIPPING_TRAP: + GrippingTrap.trigger( cell, mob ); + break; + + case Terrain.SUMMONING_TRAP: + SummoningTrap.trigger( cell, mob ); + break; + + case Terrain.DOOR: + Door.enter( cell ); + + default: + trap = false; + } + + if (trap) { + if (Dungeon.visible[cell]) { + Sample.INSTANCE.play( Assets.SND_TRAP ); + } + set( cell, Terrain.INACTIVE_TRAP ); + GameScene.updateMap( cell ); + } + + Plant plant = plants.get( cell ); + if (plant != null) { + plant.activate( mob ); + } + } + + public boolean[] updateFieldOfView( Char c ) { + + int cx = c.pos % WIDTH; + int cy = c.pos / WIDTH; + + boolean sighted = c.buff( Blindness.class ) == null && c.buff( Shadows.class ) == null && c.isAlive(); + if (sighted) { + ShadowCaster.castShadow( cx, cy, fieldOfView, c.viewDistance ); + } else { + Arrays.fill( fieldOfView, false ); + } + + int sense = 1; + if (c.isAlive()) { + for (Buff b : c.buffs( MindVision.class )) { + sense = Math.max( ((MindVision)b).distance, sense ); + } + } + + if ((sighted && sense > 1) || !sighted) { + + int ax = Math.max( 0, cx - sense ); + int bx = Math.min( cx + sense, WIDTH - 1 ); + int ay = Math.max( 0, cy - sense ); + int by = Math.min( cy + sense, HEIGHT - 1 ); + + int len = bx - ax + 1; + int pos = ax + ay * WIDTH; + for (int y = ay; y <= by; y++, pos+=WIDTH) { + Arrays.fill( fieldOfView, pos, pos + len, true ); + } + + for (int i=0; i < LENGTH; i++) { + fieldOfView[i] &= discoverable[i]; + } + } + + if (c.isAlive()) { + if (c.buff( MindVision.class ) != null) { + for (Mob mob : mobs) { + int p = mob.pos; + fieldOfView[p] = true; + fieldOfView[p + 1] = true; + fieldOfView[p - 1] = true; + fieldOfView[p + WIDTH + 1] = true; + fieldOfView[p + WIDTH - 1] = true; + fieldOfView[p - WIDTH + 1] = true; + fieldOfView[p - WIDTH - 1] = true; + fieldOfView[p + WIDTH] = true; + fieldOfView[p - WIDTH] = true; + } + } else if (c == Dungeon.hero && ((Hero)c).heroClass == HeroClass.HUNTRESS) { + for (Mob mob : mobs) { + int p = mob.pos; + if (distance( c.pos, p) == 2) { + fieldOfView[p] = true; + fieldOfView[p + 1] = true; + fieldOfView[p - 1] = true; + fieldOfView[p + WIDTH + 1] = true; + fieldOfView[p + WIDTH - 1] = true; + fieldOfView[p - WIDTH + 1] = true; + fieldOfView[p - WIDTH - 1] = true; + fieldOfView[p + WIDTH] = true; + fieldOfView[p - WIDTH] = true; + } + } + } + if (c.buff( Awareness.class ) != null) { + + for (Heap heap : heaps.values()) { + int p = heap.pos; + fieldOfView[p] = true; + fieldOfView[p + 1] = true; + fieldOfView[p - 1] = true; + fieldOfView[p + WIDTH + 1] = true; + fieldOfView[p + WIDTH - 1] = true; + fieldOfView[p - WIDTH + 1] = true; + fieldOfView[p - WIDTH - 1] = true; + fieldOfView[p + WIDTH] = true; + fieldOfView[p - WIDTH] = true; + } + } + } + + return fieldOfView; + } + + public static int distance( int a, int b ) { + int ax = a % WIDTH; + int ay = a / WIDTH; + int bx = b % WIDTH; + int by = b / WIDTH; + return Math.max( Math.abs( ax - bx ), Math.abs( ay - by ) ); + } + + public static boolean adjacent( int a, int b ) { + int diff = Math.abs( a - b ); + return diff == 1 || diff == WIDTH || diff == WIDTH + 1 || diff == WIDTH - 1; + } + + public String tileName( int tile ) { + + if (tile >= Terrain.WATER_TILES) { + return tileName( Terrain.WATER ); + } + + if (tile != Terrain.CHASM && (Terrain.flags[tile] & Terrain.PIT) != 0) { + return tileName( Terrain.CHASM ); + } + + switch (tile) { + case Terrain.CHASM: + return "Chasm"; + case Terrain.EMPTY: + case Terrain.EMPTY_SP: + case Terrain.EMPTY_DECO: + case Terrain.SECRET_TOXIC_TRAP: + case Terrain.SECRET_FIRE_TRAP: + case Terrain.SECRET_PARALYTIC_TRAP: + case Terrain.SECRET_POISON_TRAP: + case Terrain.SECRET_ALARM_TRAP: + case Terrain.SECRET_LIGHTNING_TRAP: + return "Floor"; + case Terrain.GRASS: + return "Grass"; + case Terrain.WATER: + return "Water"; + case Terrain.WALL: + case Terrain.WALL_DECO: + case Terrain.SECRET_DOOR: + return "Wall"; + case Terrain.DOOR: + return "Closed door"; + case Terrain.OPEN_DOOR: + return "Open door"; + case Terrain.ENTRANCE: + return "Depth entrance"; + case Terrain.EXIT: + return "Depth exit"; + case Terrain.EMBERS: + return "Embers"; + case Terrain.LOCKED_DOOR: + return "Locked door"; + case Terrain.PEDESTAL: + return "Pedestal"; + case Terrain.BARRICADE: + return "Barricade"; + case Terrain.HIGH_GRASS: + return "High grass"; + case Terrain.LOCKED_EXIT: + return "Locked depth exit"; + case Terrain.UNLOCKED_EXIT: + return "Unlocked depth exit"; + case Terrain.SIGN: + return "Sign"; + case Terrain.WELL: + return "Well"; + case Terrain.EMPTY_WELL: + return "Empty well"; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "Statue"; + case Terrain.TOXIC_TRAP: + return "Toxic gas trap"; + case Terrain.FIRE_TRAP: + return "Fire trap"; + case Terrain.PARALYTIC_TRAP: + return "Paralytic gas trap"; + case Terrain.POISON_TRAP: + return "Poison dart trap"; + case Terrain.ALARM_TRAP: + return "Alarm trap"; + case Terrain.LIGHTNING_TRAP: + return "Lightning trap"; + case Terrain.GRIPPING_TRAP: + return "Gripping trap"; + case Terrain.SUMMONING_TRAP: + return "Summoning trap"; + case Terrain.INACTIVE_TRAP: + return "Triggered trap"; + case Terrain.BOOKSHELF: + return "Bookshelf"; + case Terrain.ALCHEMY: + return "Alchemy pot"; + default: + return "???"; + } + } + + public String tileDesc( int tile ) { + + switch (tile) { + case Terrain.CHASM: + return "You can't see the bottom."; + case Terrain.WATER: + return "In case of burning step into the water to extinguish the fire."; + case Terrain.ENTRANCE: + return "Stairs lead up to the upper depth."; + case Terrain.EXIT: + case Terrain.UNLOCKED_EXIT: + return "Stairs lead down to the lower depth."; + case Terrain.EMBERS: + return "Embers cover the floor."; + case Terrain.HIGH_GRASS: + return "Dense vegetation blocks the view."; + case Terrain.LOCKED_DOOR: + return "This door is locked, you need a matching key to unlock it."; + case Terrain.LOCKED_EXIT: + return "Heavy bars block the stairs leading down."; + case Terrain.BARRICADE: + return "The wooden barricade is firmly set but has dried over the years. Might it burn?"; + case Terrain.SIGN: + return "You can't read the text from here."; + case Terrain.TOXIC_TRAP: + case Terrain.FIRE_TRAP: + case Terrain.PARALYTIC_TRAP: + case Terrain.POISON_TRAP: + case Terrain.ALARM_TRAP: + case Terrain.LIGHTNING_TRAP: + case Terrain.GRIPPING_TRAP: + case Terrain.SUMMONING_TRAP: + return "Stepping onto a hidden pressure plate will activate the trap."; + case Terrain.INACTIVE_TRAP: + return "The trap has been triggered before and it's not dangerous anymore."; + case Terrain.STATUE: + case Terrain.STATUE_SP: + return "Someone wanted to adorn this place, but failed, obviously."; + case Terrain.ALCHEMY: + return "Drop some seeds here to cook a potion."; + case Terrain.EMPTY_WELL: + return "The well has run dry."; + default: + if (tile >= Terrain.WATER_TILES) { + return tileDesc( Terrain.WATER ); + } + if ((Terrain.flags[tile] & Terrain.PIT) != 0) { + return tileDesc( Terrain.CHASM ); + } + return ""; + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/Patch.java b/src/com/watabou/pixeldungeon/levels/Patch.java new file mode 100644 index 0000000000..85563a8314 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/Patch.java @@ -0,0 +1,85 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.utils.Random; + +public class Patch { + + private static boolean[] cur = new boolean[Level.LENGTH]; + private static boolean[] off = new boolean[Level.LENGTH]; + + public static boolean[] generate( float seed, int nGen ) { + + int w = Level.WIDTH; + int h = Level.HEIGHT; + + for (int i=0; i < Level.LENGTH; i++) { + off[i] = Random.Float() < seed; + } + + for (int i=0; i < nGen; i++) { + + for (int y=1; y < h-1; y++) { + for (int x=1; x < w-1; x++) { + + int pos = x + y * w; + int count = 0; + if (off[pos-w-1]) { + count++; + } + if (off[pos-w]) { + count++; + } + if (off[pos-w+1]) { + count++; + } + if (off[pos-1]) { + count++; + } + if (off[pos+1]) { + count++; + } + if (off[pos+w-1]) { + count++; + } + if (off[pos+w]) { + count++; + } + if (off[pos+w+1]) { + count++; + } + + if (!off[pos] && count >= 5) { + cur[pos] = true; + } else if (off[pos] && count >= 4) { + cur[pos] = true; + } else { + cur[pos] = false; + } + } + } + + boolean[] tmp = cur; + cur = off; + off = tmp; + } + + return off; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/PrisonBossLevel.java b/src/com/watabou/pixeldungeon/levels/PrisonBossLevel.java new file mode 100644 index 0000000000..8d27cf5c19 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/PrisonBossLevel.java @@ -0,0 +1,376 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.List; + +import com.watabou.noosa.Scene; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.items.keys.SkeletonKey; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.pixeldungeon.levels.painters.Painter; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Bundle; +import com.watabou.utils.Graph; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class PrisonBossLevel extends RegularLevel { + + { + color1 = 0x6a723d; + color2 = 0x88924c; + } + + private Room anteroom; + private int arenaDoor; + + private boolean enteredArena = false; + private boolean keyDropped = false; + + @Override + public String tilesTex() { + return Assets.TILES_PRISON; + } + + @Override + public String waterTex() { + return Assets.WATER_PRISON; + } + + private static final String ARENA = "arena"; + private static final String DOOR = "door"; + private static final String ENTERED = "entered"; + private static final String DROPPED = "droppped"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( ARENA, roomExit ); + bundle.put( DOOR, arenaDoor ); + bundle.put( ENTERED, enteredArena ); + bundle.put( DROPPED, keyDropped ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + roomExit = (Room)bundle.get( ARENA ); + arenaDoor = bundle.getInt( DOOR ); + enteredArena = bundle.getBoolean( ENTERED ); + keyDropped = bundle.getBoolean( DROPPED ); + } + + @Override + protected boolean build() { + + initRooms(); + + int distance; + int retry = 0; + + do { + + if (retry++ > 10) { + return false; + } + + int innerRetry = 0; + do { + if (innerRetry++ > 10) { + return false; + } + roomEntrance = Random.element( rooms ); + } while (roomEntrance.width() < 4 || roomEntrance.height() < 4); + + innerRetry = 0; + do { + if (innerRetry++ > 10) { + return false; + } + roomExit = Random.element( rooms ); + } while ( + roomExit == roomEntrance || + roomExit.width() < 7 || + roomExit.height() < 7 || + roomExit.top == 0); + + Graph.buildDistanceMap( rooms, roomExit ); + distance = Graph.buildPath( rooms, roomEntrance, roomExit ).size(); + + } while (distance < 3); + + roomEntrance.type = Type.ENTRANCE; + roomExit.type = Type.BOSS_EXIT; + + List path = Graph.buildPath( rooms, roomEntrance, roomExit ); + Graph.setPrice( path, roomEntrance.distance ); + + Graph.buildDistanceMap( rooms, roomExit ); + path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + anteroom = path.get( path.size() - 2 ); + anteroom.type = Type.STANDARD; + + Room room = roomEntrance; + for (Room next : path) { + room.connect( next ); + room = next; + } + + for (Room r : rooms) { + if (r.type == Type.NULL && r.connected.size() > 0) { + r.type = Type.PASSAGE; + } + } + + paint(); + + Room r = (Room)roomExit.connected.keySet().toArray()[0]; + if (roomExit.connected.get( r ).y == roomExit.top) { + return false; + } + + paintWater(); + paintGrass(); + + placeTraps(); + + return true; + } + + protected boolean[] water() { + return Patch.generate( 0.45f, 5 ); + } + + protected boolean[] grass() { + return Patch.generate( 0.30f, 4 ); + } + + protected void paintDoors( Room r ) { + + for (Room n : r.connected.keySet()) { + + if (r.type == Type.NULL) { + continue; + } + + Point door = r.connected.get( n ); + + if (r.type == Room.Type.PASSAGE && n.type == Room.Type.PASSAGE) { + + Painter.set( this, door, Terrain.EMPTY ); + + } else { + + Painter.set( this, door, Terrain.DOOR ); + + } + + } + } + + @Override + protected void placeTraps() { + + int nTraps = nTraps(); + + for (int i=0; i < nTraps; i++) { + + int trapPos = Random.Int( LENGTH ); + + if (map[trapPos] == Terrain.EMPTY) { + map[trapPos] = Terrain.POISON_TRAP; + } + } + } + + @Override + protected void decorate() { + + for (int i=WIDTH + 1; i < LENGTH - WIDTH - 1; i++) { + if (map[i] == Terrain.EMPTY) { + + float c = 0.15f; + if (map[i + 1] == Terrain.WALL && map[i + WIDTH] == Terrain.WALL) { + c += 0.2f; + } + if (map[i - 1] == Terrain.WALL && map[i + WIDTH] == Terrain.WALL) { + c += 0.2f; + } + if (map[i + 1] == Terrain.WALL && map[i - WIDTH] == Terrain.WALL) { + c += 0.2f; + } + if (map[i - 1] == Terrain.WALL && map[i - WIDTH] == Terrain.WALL) { + c += 0.2f; + } + + if (Random.Float() < c) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + for (int i=0; i < WIDTH; i++) { + if (map[i] == Terrain.WALL && + (map[i + WIDTH] == Terrain.EMPTY || map[i + WIDTH] == Terrain.EMPTY_SP) && + Random.Int( 4 ) == 0) { + + map[i] = Terrain.WALL_DECO; + } + } + + for (int i=WIDTH; i < LENGTH - WIDTH; i++) { + if (map[i] == Terrain.WALL && + map[i - WIDTH] == Terrain.WALL && + (map[i + WIDTH] == Terrain.EMPTY || map[i + WIDTH] == Terrain.EMPTY_SP) && + Random.Int( 2 ) == 0) { + + map[i] = Terrain.WALL_DECO; + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + + Point door = roomExit.entrance(); + arenaDoor = door.x + door.y * WIDTH; + Painter.set( this, arenaDoor, Terrain.LOCKED_DOOR ); + + Painter.fill( this, + roomExit.left + 2, + roomExit.top + 2, + roomExit.width() - 3, + roomExit.height() - 3, + Terrain.INACTIVE_TRAP ); + } + + @Override + protected void createMobs() { + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + + int keyPos = anteroom.random(); + while (!passable[keyPos]) { + keyPos = anteroom.random(); + } + drop( new IronKey( Dungeon.depth ), keyPos ).type = Heap.Type.CHEST; + + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = roomEntrance.random(); + } while (pos == entrance || map[pos] == Terrain.SIGN); + drop( item, pos ).type = Heap.Type.SKELETON; + } + } + + @Override + public void press( int cell, Char ch ) { + + super.press( cell, ch ); + + if (ch == Dungeon.hero && !enteredArena && roomExit.inside( cell )) { + + enteredArena = true; + + int pos; + do { + pos = roomExit.random(); + } while (pos == cell || Actor.findChar( pos ) != null); + + Mob boss = Bestiary.mob( Dungeon.depth ); + boss.state = Mob.State.HUNTING; + boss.pos = pos; + GameScene.add( boss ); + boss.notice(); + + mobPress( boss ); + + set( arenaDoor, Terrain.LOCKED_DOOR ); + GameScene.updateMap( arenaDoor ); + Dungeon.observe(); + } + } + + @Override + public Heap drop( Item item, int cell ) { + + if (!keyDropped && item instanceof SkeletonKey) { + + keyDropped = true; + + set( arenaDoor, Terrain.DOOR ); + GameScene.updateMap( arenaDoor ); + Dungeon.observe(); + } + + return super.drop( item, cell ); + } + + @Override + public int randomRespawnCell() { + return -1; + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Dark cold water."; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.EMPTY_DECO: + return "There are old blood stains on the floor."; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + PrisonLevel.addVisuals( this, scene ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/PrisonLevel.java b/src/com/watabou/pixeldungeon/levels/PrisonLevel.java new file mode 100644 index 0000000000..92773a7a92 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/PrisonLevel.java @@ -0,0 +1,189 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Scene; +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.mobs.npcs.Wandmaker; +import com.watabou.pixeldungeon.effects.Halo; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class PrisonLevel extends RegularLevel { + + { + color1 = 0x6a723d; + color2 = 0x88924c; + } + + @Override + public String tilesTex() { + return Assets.TILES_PRISON; + } + + @Override + public String waterTex() { + return Assets.WATER_PRISON; + } + + protected boolean[] water() { + return Patch.generate( feeling == Feeling.WATER ? 0.65f : 0.45f, 4 ); + } + + protected boolean[] grass() { + return Patch.generate( feeling == Feeling.GRASS ? 0.60f : 0.40f, 3 ); + } + + @Override + protected void assignRoomType() { + super.assignRoomType(); + + for (Room r : rooms) { + if (r.type == Type.TUNNEL) { + r.type = Type.PASSAGE; + } + } + } + + @Override + protected void createMobs() { + super.createMobs(); + + Wandmaker.Quest.spawn( this, roomEntrance ); + } + + @Override + protected void decorate() { + + for (int i=WIDTH + 1; i < LENGTH - WIDTH - 1; i++) { + if (map[i] == Terrain.EMPTY) { + + float c = 0.05f; + if (map[i + 1] == Terrain.WALL && map[i + WIDTH] == Terrain.WALL) { + c += 0.2f; + } + if (map[i - 1] == Terrain.WALL && map[i + WIDTH] == Terrain.WALL) { + c += 0.2f; + } + if (map[i + 1] == Terrain.WALL && map[i - WIDTH] == Terrain.WALL) { + c += 0.2f; + } + if (map[i - 1] == Terrain.WALL && map[i - WIDTH] == Terrain.WALL) { + c += 0.2f; + } + + if (Random.Float() < c) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + for (int i=0; i < WIDTH; i++) { + if (map[i] == Terrain.WALL && + (map[i + WIDTH] == Terrain.EMPTY || map[i + WIDTH] == Terrain.EMPTY_SP) && + Random.Int( 6 ) == 0) { + + map[i] = Terrain.WALL_DECO; + } + } + + for (int i=WIDTH; i < LENGTH - WIDTH; i++) { + if (map[i] == Terrain.WALL && + map[i - WIDTH] == Terrain.WALL && + (map[i + WIDTH] == Terrain.EMPTY || map[i + WIDTH] == Terrain.EMPTY_SP) && + Random.Int( 3 ) == 0) { + + map[i] = Terrain.WALL_DECO; + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Dark cold water."; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.EMPTY_DECO: + return "There are old blood stains on the floor."; + case Terrain.BOOKSHELF: + return "This is probably a vestige of a prison library. Might it burn?"; + default: + return super.tileDesc( tile ); + } + } + + @Override + public void addVisuals( Scene scene ) { + super.addVisuals( scene ); + addVisuals( this, scene ); + } + + public static void addVisuals( Level level, Scene scene ) { + for (int i=0; i < LENGTH; i++) { + if (level.map[i] == Terrain.WALL_DECO) { + scene.add( new Torch( i ) ); + } + } + } + + private static class Torch extends Emitter { + + private int pos; + + public Torch( int pos ) { + super(); + + this.pos = pos; + + PointF p = DungeonTilemap.tileCenterToWorld( pos ); + pos( p.x - 1, p.y + 3, 2, 0 ); + + pour( FlameParticle.FACTORY, 0.15f ); + + add( new Halo( 16, 0xFFFFCC, 0.2f ).point( p.x, p.y ) ); + } + + @Override + public void update() { + if (visible = Dungeon.visible[pos]) { + super.update(); + } + } + } +} \ No newline at end of file diff --git a/src/com/watabou/pixeldungeon/levels/RegularLevel.java b/src/com/watabou/pixeldungeon/levels/RegularLevel.java new file mode 100644 index 0000000000..94b0b0025e --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/RegularLevel.java @@ -0,0 +1,696 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.pixeldungeon.levels.painters.*; +import com.watabou.utils.Bundle; +import com.watabou.utils.Graph; +import com.watabou.utils.Random; +import com.watabou.utils.Rect; + +public abstract class RegularLevel extends Level { + + protected HashSet rooms; + + protected Room roomEntrance; + protected Room roomExit; + + protected ArrayList specials; + + public int secretDoors; + + @Override + protected boolean build() { + + if (!initRooms()) { + return false; + } + + int distance; + int retry = 0; + int minDistance = (int)Math.sqrt( rooms.size() ); + do { + do { + roomEntrance = Random.element( rooms ); + } while (roomEntrance.width() < 4 || roomEntrance.height() < 4); + + do { + roomExit = Random.element( rooms ); + } while (roomExit == roomEntrance || roomExit.width() < 4 || roomExit.height() < 4); + + Graph.buildDistanceMap( rooms, roomExit ); + distance = roomEntrance.distance(); + + if (retry++ > 10) { + return false; + } + + } while (distance < minDistance); + + roomEntrance.type = Type.ENTRANCE; + roomExit.type = Type.EXIT; + + HashSet connected = new HashSet(); + connected.add( roomEntrance ); + + Graph.buildDistanceMap( rooms, roomExit ); + List path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + Room room = roomEntrance; + for (Room next : path) { + room.connect( next ); + room = next; + connected.add( room ); + } + + Graph.setPrice( path, roomEntrance.distance ); + + Graph.buildDistanceMap( rooms, roomExit ); + path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + room = roomEntrance; + for (Room next : path) { + room.connect( next ); + room = next; + connected.add( room ); + } + + int nConnected = (int)(rooms.size() * Random.Float( 0.5f, 0.7f )); + while (connected.size() < nConnected) { + + Room cr = Random.element( connected ); + Room or = Random.element( cr.neigbours ); + if (!connected.contains( or )) { + + cr.connect( or ); + connected.add( or ); + } + } + + if (Dungeon.shopOnLevel()) { + Room shop = null; + for (Room r : roomEntrance.connected.keySet()) { + if (r.connected.size() == 1 && r.width() >= 5 && r.height() >= 5) { + shop = r; + break; + } + } + + if (shop == null) { + return false; + } else { + shop.type = Room.Type.SHOP; + } + } + + specials = new ArrayList( Room.SPECIALS ); + if (Dungeon.bossLevel( Dungeon.depth + 1 )) { + specials.remove( Room.Type.WEAK_FLOOR ); + } + assignRoomType(); + + paint(); + paintWater(); + paintGrass(); + + placeTraps(); + + return true; + } + + protected boolean initRooms() { + + rooms = new HashSet(); + split( new Rect( 0, 0, WIDTH - 1, HEIGHT - 1 ) ); + + if (rooms.size() < 8) { + return false; + } + + Room[] ra = rooms.toArray( new Room[0] ); + for (int i=0; i < ra.length-1; i++) { + for (int j=i+1; j < ra.length; j++) { + ra[i].addNeigbour( ra[j] ); + } + } + + return true; + } + + protected void assignRoomType() { + + int specialRooms = 0; + + for (Room r : rooms) { + if (r.type == Type.NULL && + r.connected.size() == 1) { + + if (specials.size() > 0 && + r.width() > 3 && r.height() > 3 && + Random.Int( specialRooms * specialRooms + 2 ) == 0) { + + if (pitRoomNeeded) { + + r.type = Type.PIT; + pitRoomNeeded = false; + + specials.remove( Type.ARMORY ); + specials.remove( Type.CRYPT ); + specials.remove( Type.LABORATORY ); + specials.remove( Type.LIBRARY ); + specials.remove( Type.STATUE ); + specials.remove( Type.TREASURY ); + specials.remove( Type.VAULT ); + specials.remove( Type.WEAK_FLOOR ); + + } else if (Dungeon.depth % 5 == 2 && specials.contains( Type.LABORATORY )) { + + r.type = Type.LABORATORY; + + } else if (Dungeon.depth >= Dungeon.transmutation && specials.contains( Type.MAGIC_WELL )) { + + r.type = Type.MAGIC_WELL; + + } else { + + int n = specials.size(); + r.type = specials.get( Math.min( Random.Int( n ), Random.Int( n ) ) ); + if (r.type == Type.WEAK_FLOOR) { + weakFloorCreated = true; + } + + } + + Room.useType( r.type ); + specials.remove( r.type ); + specialRooms++; + + } else if (Random.Int( 2 ) == 0){ + + HashSet neigbours = new HashSet(); + for (Room n : r.neigbours) { + if (!r.connected.containsKey( n ) && + !Room.SPECIALS.contains( n.type ) && + n.type != Type.PIT) { + + neigbours.add( n ); + } + } + if (neigbours.size() > 1) { + r.connect( Random.element( neigbours ) ); + } + } + } + } + + int count = 0; + for (Room r : rooms) { + if (r.type == Type.NULL) { + int connections = r.connected.size(); + if (connections == 0) { + + } else if (Random.Int( connections * connections ) == 0) { + r.type = Type.STANDARD; + count++; + } else { + r.type = Type.TUNNEL; + } + } + } + + while (count < 4) { + Room r = randomRoom( Type.TUNNEL, 1 ); + if (r != null) { + r.type = Type.STANDARD; + count++; + } + } + } + + protected void paintWater() { + boolean[] lake = water(); + for (int i=0; i < LENGTH; i++) { + if (map[i] == Terrain.EMPTY && lake[i]) { + map[i] = Terrain.WATER; + } + } + } + + protected void paintGrass() { + boolean[] grass = grass(); + + if (feeling == Feeling.GRASS) { + + for (Room room : rooms) { + if (room.type != Type.NULL && room.type != Type.PASSAGE && room.type != Type.TUNNEL) { + grass[(room.left + 1) + (room.top + 1) * WIDTH] = true; + grass[(room.right - 1) + (room.top + 1) * WIDTH] = true; + grass[(room.left + 1) + (room.bottom - 1) * WIDTH] = true; + grass[(room.right - 1) + (room.bottom - 1) * WIDTH] = true; + } + } + } + + for (int i=WIDTH+1; i < LENGTH-WIDTH-1; i++) { + if (map[i] == Terrain.EMPTY && grass[i]) { + int count = 1; + for (int n : NEIGHBOURS8) { + if (grass[i + n]) { + count++; + } + } + map[i] = (Random.Float() < count / 12f) ? Terrain.HIGH_GRASS : Terrain.GRASS; + } + } + } + + protected abstract boolean[] water(); + protected abstract boolean[] grass(); + + protected void placeTraps() { + + int nTraps = nTraps(); + float[] trapChances = trapChances(); + + for (int i=0; i < nTraps; i++) { + + int trapPos = Random.Int( LENGTH ); + + if (map[trapPos] == Terrain.EMPTY) { + switch (Random.chances( trapChances )) { + case 0: + map[trapPos] = Terrain.SECRET_TOXIC_TRAP; + break; + case 1: + map[trapPos] = Terrain.SECRET_FIRE_TRAP; + break; + case 2: + map[trapPos] = Terrain.SECRET_PARALYTIC_TRAP; + break; + case 3: + map[trapPos] = Terrain.SECRET_POISON_TRAP; + break; + case 4: + map[trapPos] = Terrain.SECRET_ALARM_TRAP; + break; + case 5: + map[trapPos] = Terrain.SECRET_LIGHTNING_TRAP; + break; + case 6: + map[trapPos] = Terrain.SECRET_GRIPPING_TRAP; + break; + case 7: + map[trapPos] = Terrain.SECRET_SUMMONING_TRAP; + break; + } + } + } + } + + protected int nTraps() { + return Dungeon.depth <= 1 ? 0 : Random.Int( 1, rooms.size() + Dungeon.depth ); + } + + protected float[] trapChances() { + float[] chances = { 1, 1, 1, 1, 1, 1, 1, 1 }; + return chances; + } + + protected int minRoomSize = 7; + protected int maxRoomSize = 9; + + protected void split( Rect rect ) { + + int w = rect.width(); + int h = rect.height(); + + if (w > maxRoomSize && h < minRoomSize) { + + int vw = Random.Int( rect.left + 3, rect.right - 3 ); + split( new Rect( rect.left, rect.top, vw, rect.bottom ) ); + split( new Rect( vw, rect.top, rect.right, rect.bottom ) ); + + } else + if (h > maxRoomSize && w < minRoomSize) { + + int vh = Random.Int( rect.top + 3, rect.bottom - 3 ); + split( new Rect( rect.left, rect.top, rect.right, vh ) ); + split( new Rect( rect.left, vh, rect.right, rect.bottom ) ); + + } else + if ((Math.random() <= (minRoomSize * minRoomSize / rect.square()) && w <= maxRoomSize && h <= maxRoomSize) || w < minRoomSize || h < minRoomSize) { + + rooms.add( (Room)new Room().set( rect ) ); + + } else { + + if (Random.Float() < (float)(w - 2) / (w + h - 4)) { + int vw = Random.Int( rect.left + 3, rect.right - 3 ); + split( new Rect( rect.left, rect.top, vw, rect.bottom ) ); + split( new Rect( vw, rect.top, rect.right, rect.bottom ) ); + } else { + int vh = Random.Int( rect.top + 3, rect.bottom - 3 ); + split( new Rect( rect.left, rect.top, rect.right, vh ) ); + split( new Rect( rect.left, vh, rect.right, rect.bottom ) ); + } + + } + } + + protected void paint() { + + for (Room r : rooms) { + if (r.type != Type.NULL) { + placeDoors( r ); + r.type.paint( this, r ); + } else { + if (feeling == Feeling.CHASM && Random.Int( 2 ) == 0) { + Painter.fill( this, r, Terrain.WALL ); + } + } + } + + for (Room r : rooms) { + paintDoors( r ); + } + } + + private void placeDoors( Room r ) { + for (Room n : r.connected.keySet()) { + Room.Door door = r.connected.get( n ); + if (door == null) { + + Rect i = r.intersect( n ); + if (i.width() == 0) { + door = new Room.Door( + i.left, + Random.Int( i.top + 1, i.bottom ) ); + } else { + door = new Room.Door( + Random.Int( i.left + 1, i.right ), + i.top); + } + + r.connected.put( n, door ); + n.connected.put( r, door ); + } + } + } + + protected void paintDoors( Room r ) { + for (Room n : r.connected.keySet()) { + + if (joinRooms( r, n )) { + continue; + } + + Room.Door d = r.connected.get( n ); + int door = d.x + d.y * WIDTH; + + switch (d.type) { + case EMPTY: + map[door] = Terrain.EMPTY; + break; + case TUNNEL: + map[door] = tunnelTile(); + break; + case REGULAR: + if (Dungeon.depth <= 1) { + map[door] = Terrain.DOOR; + } else { + boolean secret = (Dungeon.depth < 6 ? Random.Int( 12 - Dungeon.depth ) : Random.Int( 6 )) == 0; + map[door] = secret ? Terrain.SECRET_DOOR : Terrain.DOOR; + if (secret) { + secretDoors++; + } + } + break; + case UNLOCKED: + map[door] = Terrain.DOOR; + break; + case HIDDEN: + map[door] = Terrain.SECRET_DOOR; + break; + case BARRICADE: + map[door] = Random.Int( 3 ) == 0 ? Terrain.BOOKSHELF : Terrain.BARRICADE; + break; + case LOCKED: + map[door] = Terrain.LOCKED_DOOR; + break; + } + } + } + + protected boolean joinRooms( Room r, Room n ) { + + if (r.type != Room.Type.STANDARD || n.type != Room.Type.STANDARD) { + return false; + } + + Rect w = r.intersect( n ); + if (w.left == w.right) { + + if (w.bottom - w.top < 3) { + return false; + } + + if (w.height() == Math.max( r.height(), n.height() )) { + return false; + } + + if (r.width() + n.width() > maxRoomSize) { + return false; + } + + w.top += 1; + w.bottom -= 0; + + w.right++; + + Painter.fill( this, w.left, w.top, 1, w.height(), Terrain.EMPTY ); + + } else { + + if (w.right - w.left < 3) { + return false; + } + + if (w.width() == Math.max( r.width(), n.width() )) { + return false; + } + + if (r.height() + n.height() > maxRoomSize) { + return false; + } + + w.left += 1; + w.right -= 0; + + w.bottom++; + + Painter.fill( this, w.left, w.top, w.width(), 1, Terrain.EMPTY ); + } + + return true; + } + + @Override + public int nMobs() { + return 2 + Dungeon.depth % 5 + Random.Int( 3 ); + } + + @Override + protected void createMobs() { + int nMobs = nMobs(); + for (int i=0; i < nMobs; i++) { + Mob mob = Bestiary.mob( Dungeon.depth ); + do { + mob.pos = randomRespawnCell(); + } while (mob.pos == -1); + mobs.add( mob ); + Actor.occupyCell( mob ); + } + } + + @Override + public int randomRespawnCell() { + int count = 0; + int cell = -1; + + while (true) { + + if (++count > 10) { + return -1; + } + + Room room = randomRoom( Room.Type.STANDARD, 10 ); + if (room == null) { + continue; + } + + cell = room.random(); + if (!Dungeon.visible[cell] && Actor.findChar( cell ) == null && Level.passable[cell]) { + return cell; + } + + } + } + + @Override + public int randomDestination() { + + int cell = -1; + + while (true) { + + Room room = Random.element( rooms ); + if (room == null) { + continue; + } + + cell = room.random(); + if (Level.passable[cell]) { + return cell; + } + + } + } + + @Override + protected void createItems() { + + int nItems = 3; + while (Random.Float() < 0.3f) { + nItems++; + } + + for (int i=0; i < nItems; i++) { + Heap.Type type = null; + switch (Random.Int( 20 )) { + case 0: + type = Heap.Type.SKELETON; + break; + case 1: + case 2: + case 3: + case 4: + type = Heap.Type.CHEST; + break; + default: + type = Heap.Type.HEAP; + } + drop( Generator.random(), randomDropCell() ).type = type; + } + + for (Item item : itemsToSpawn) { + int cell = randomDropCell(); + if (item instanceof ScrollOfUpgrade) { + while (map[cell] == Terrain.FIRE_TRAP || map[cell] == Terrain.SECRET_FIRE_TRAP) { + cell = randomDropCell(); + } + } + drop( item, cell ).type = Heap.Type.HEAP; + } + + Item item = Bones.get(); + if (item != null) { + drop( item, randomDropCell() ).type = Heap.Type.SKELETON; + } + } + + protected Room randomRoom( Room.Type type, int tries ) { + for (int i=0; i < tries; i++) { + Room room = Random.element( rooms ); + if (room.type == type) { + return room; + } + } + return null; + } + + public Room room( int pos ) { + for (Room room : rooms) { + if (room.type != Type.NULL && room.inside( pos )) { + return room; + } + } + + return null; + } + + protected int randomDropCell() { + while (true) { + Room room = randomRoom( Room.Type.STANDARD, 1 ); + if (room != null) { + int pos = room.random(); + if (passable[pos]) { + return pos; + } + } + } + } + + @Override + public int pitCell() { + for (Room room : rooms) { + if (room.type == Type.PIT) { + return room.random(); + } + } + + return super.pitCell(); + } + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( "rooms", rooms ); + } + + @SuppressWarnings("unchecked") + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + + weakFloorCreated = false; + + rooms = new HashSet( (Collection) bundle.getCollection( "rooms" ) ); + for (Room r : rooms) { + if (r.type == Type.WEAK_FLOOR) { + weakFloorCreated = true; + break; + } + } + } + +} diff --git a/src/com/watabou/pixeldungeon/levels/Room.java b/src/com/watabou/pixeldungeon/levels/Room.java new file mode 100644 index 0000000000..b4ff9f4642 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/Room.java @@ -0,0 +1,255 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; + +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.pixeldungeon.levels.painters.*; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Graph; +import com.watabou.utils.Point; +import com.watabou.utils.Random; +import com.watabou.utils.Rect; + +public class Room extends Rect implements Graph.Node, Bundlable { + + public HashSet neigbours = new HashSet(); + public HashMap connected = new HashMap(); + + public int distance; + public int price = 1; + + public static enum Type { + NULL( null ), + STANDARD ( StandardPainter.class ), + ENTRANCE ( EntrancePainter.class ), + EXIT ( ExitPainter.class ), + BOSS_EXIT ( BossExitPainter.class ), + TUNNEL ( TunnelPainter.class ), + PASSAGE ( PassagePainter.class ), + SHOP ( ShopPainter.class ), + BLACKSMITH ( BlacksmithPainter.class ), + TREASURY ( TreasuryPainter.class ), + ARMORY ( ArmoryPainter.class ), + LIBRARY ( LibraryPainter.class ), + LABORATORY ( LaboratoryPainter.class ), + VAULT ( VaultPainter.class ), + TRAPS ( TrapsPainter.class ), + STORAGE ( StoragePainter.class ), + MAGIC_WELL ( MagicWellPainter.class ), + GARDEN ( GardenPainter.class ), + CRYPT ( CryptPainter.class ), + STATUE ( StatuePainter.class ), + POOL ( PoolPainter.class ), + RAT_KING ( RatKingPainter.class ), + WEAK_FLOOR ( WeakFloorPainter.class ), + PIT ( PitPainter.class ); + + private Method paint; + + private Type( Class painter ) { + try { + paint = painter.getMethod( "paint", Level.class, Room.class ); + } catch (Exception e) { + paint = null; + } + } + + public void paint( Level level, Room room ) { + try { + paint.invoke( null, level, room ); + } catch (Exception e) { + PixelDungeon.reportException( e ); + } + } + }; + + public static final ArrayList SPECIALS = new ArrayList( Arrays.asList( + Type.WEAK_FLOOR, Type.MAGIC_WELL, Type.CRYPT, Type.POOL, Type.GARDEN, Type.LIBRARY, Type.ARMORY, + Type.TREASURY, Type.TRAPS, Type.STORAGE, Type.STATUE, Type.LABORATORY, Type.VAULT + ) ); + + public Type type = Type.NULL; + + public int random() { + return random( 0 ); + } + + public int random( int m ) { + int x = Random.Int( left + 1 + m, right - m ); + int y = Random.Int( top + 1 + m, bottom - m ); + return x + y * Level.WIDTH; + } + + public void addNeigbour( Room other ) { + + Rect i = intersect( other ); + if ((i.width() == 0 && i.height() >= 3) || + (i.height() == 0 && i.width() >= 3)) { + neigbours.add( other ); + other.neigbours.add( this ); + } + + } + + public void connect( Room room ) { + if (!connected.containsKey( room )) { + connected.put( room, null ); + room.connected.put( this, null ); + } + } + + public Door entrance() { + return connected.values().iterator().next(); + } + + public boolean inside( int p ) { + int x = p % Level.WIDTH; + int y = p / Level.WIDTH; + return x > left && y > top && x < right && y < bottom; + } + + public Point center() { + return new Point( + (left + right) / 2 + (((right - left) & 1) == 1 ? Random.Int( 2 ) : 0), + (top + bottom) / 2 + (((bottom - top) & 1) == 1 ? Random.Int( 2 ) : 0) ); + } + + // **** Graph.Node interface **** + + @Override + public int distance() { + return distance; + } + + @Override + public void distance( int value ) { + distance = value; + } + + @Override + public int price() { + return price; + } + + @Override + public void price( int value ) { + price = value; + } + + @Override + public Collection edges() { + return neigbours; + } + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( "left", left ); + bundle.put( "top", top ); + bundle.put( "right", right ); + bundle.put( "bottom", bottom ); + bundle.put( "type", type.toString() ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + left = bundle.getInt( "left" ); + top = bundle.getInt( "top" ); + right = bundle.getInt( "right" ); + bottom = bundle.getInt( "bottom" ); + type = Type.valueOf( bundle.getString( "type" ) ); + } + + public static void shuffleTypes() { + int size = SPECIALS.size(); + for (int i=0; i < size - 1; i++) { + int j = Random.Int( i, size ); + if (j != i) { + Type t = SPECIALS.get( i ); + SPECIALS.set( i, SPECIALS.get( j ) ); + SPECIALS.set( j, t ); + } + } + } + + public static void useType( Type type ) { + if (SPECIALS.remove( type )) { + SPECIALS.add( type ); + } + } + + private static final String ROOMS = "rooms"; + + public static void restoreRoomsFromBundle( Bundle bundle ) { + if (bundle.contains( ROOMS )) { + SPECIALS.clear(); + for (String type : bundle.getStringArray( ROOMS )) { + SPECIALS.add( Type.valueOf( type )); + } + } else { + shuffleTypes(); + } + } + + public static void storeRoomsInBundle( Bundle bundle ) { + String[] array = new String[SPECIALS.size()]; + for (int i=0; i < array.length; i++) { + array[i] = SPECIALS.get( i ).toString(); + } + bundle.put( ROOMS, array ); + } + + public static class Door extends Point { + + public static enum Type { + EMPTY, TUNNEL, REGULAR, UNLOCKED, HIDDEN, BARRICADE, LOCKED + } + public Type type = Type.EMPTY; + + public Door( int x, int y ) { + super( x, y ); + } + + public void set( Type type ) { + if (type.compareTo( this.type ) > 0) { + this.type = type; + } + } + } +} + + + + + + + + + + + + + + diff --git a/src/com/watabou/pixeldungeon/levels/SewerBossLevel.java b/src/com/watabou/pixeldungeon/levels/SewerBossLevel.java new file mode 100644 index 0000000000..d3e0a45a33 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/SewerBossLevel.java @@ -0,0 +1,257 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import java.util.ArrayList; +import java.util.List; + +import com.watabou.noosa.Scene; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Bones; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Room.Type; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Bundle; +import com.watabou.utils.Graph; +import com.watabou.utils.Random; + +public class SewerBossLevel extends RegularLevel { + + { + color1 = 0x48763c; + color2 = 0x59994a; + } + + private int stairs = 0; + + @Override + public String tilesTex() { + return Assets.TILES_SEWERS; + } + + @Override + public String waterTex() { + return Assets.WATER_SEWERS; + } + + @Override + protected boolean build() { + + initRooms(); + + int distance; + int retry = 0; + int minDistance = (int)Math.sqrt( rooms.size() ); + do { + int innerRetry = 0; + do { + if (innerRetry++ > 10) { + return false; + } + roomEntrance = Random.element( rooms ); + } while (roomEntrance.width() < 4 || roomEntrance.height() < 4); + + innerRetry = 0; + do { + if (innerRetry++ > 10) { + return false; + } + roomExit = Random.element( rooms ); + } while (roomExit == roomEntrance || roomExit.width() < 6 || roomExit.height() < 6 || roomExit.top == 0); + + Graph.buildDistanceMap( rooms, roomExit ); + distance = roomEntrance.distance(); + + if (retry++ > 10) { + return false; + } + + } while (distance < minDistance); + + roomEntrance.type = Type.ENTRANCE; + roomExit.type = Type.BOSS_EXIT; + + Graph.buildDistanceMap( rooms, roomExit ); + List path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + Graph.setPrice( path, roomEntrance.distance ); + + Graph.buildDistanceMap( rooms, roomExit ); + path = Graph.buildPath( rooms, roomEntrance, roomExit ); + + Room room = roomEntrance; + for (Room next : path) { + room.connect( next ); + room = next; + } + + room = (Room)roomExit.connected.keySet().toArray()[0]; + if (roomExit.top == room.bottom) { + return false; + } + + for (Room r : rooms) { + if (r.type == Type.NULL && r.connected.size() > 0) { + r.type = Type.TUNNEL; + } + } + + ArrayList candidates = new ArrayList(); + for (Room r : roomExit.neigbours) { + if (!roomExit.connected.containsKey( r ) && + (roomExit.left == r.right || roomExit.right == r.left || roomExit.bottom == r.top)) { + candidates.add( r ); + } + } + if (candidates.size() > 0) { + Room kingsRoom = Random.element( candidates ); + kingsRoom.connect( roomExit ); + kingsRoom.type = Room.Type.RAT_KING; + } + + paint(); + + paintWater(); + paintGrass(); + + placeTraps(); + + return true; + } + + protected boolean[] water() { + return Patch.generate( 0.5f, 5 ); + } + + protected boolean[] grass() { + return Patch.generate( 0.40f, 4 ); + } + + @Override + protected void decorate() { + int start = roomExit.top * WIDTH + roomExit.left + 1; + int end = start + roomExit.width() - 1; + for (int i=start; i < end; i++) { + if (i != exit) { + map[i] = Terrain.WALL_DECO; + map[i + WIDTH] = Terrain.WATER; + } else { + map[i + WIDTH] = Terrain.EMPTY; + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + } + + @Override + public void addVisuals( Scene scene ) { + SewerLevel.addVisuals( this, scene ); + } + + + @Override + protected void createMobs() { + Mob mob = Bestiary.mob( Dungeon.depth ); + mob.pos = roomExit.random(); + mobs.add( mob ); + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = roomEntrance.random(); + } while (pos == entrance || map[pos] == Terrain.SIGN); + drop( item, pos ).type = Heap.Type.SKELETON; + } + } + + public void seal() { + if (entrance != 0) { + + set( entrance, Terrain.WATER_TILES ); + GameScene.updateMap( entrance ); + GameScene.ripple( entrance ); + + stairs = entrance; + entrance = 0; + } + } + + public void unseal() { + if (stairs != 0) { + + entrance = stairs; + stairs = 0; + + set( entrance, Terrain.ENTRANCE ); + GameScene.updateMap( entrance ); + } + } + + private static final String STAIRS = "stairs"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( STAIRS, stairs ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + stairs = bundle.getInt( STAIRS ); + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Murky water"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc( int tile ) { + switch (tile) { + case Terrain.EMPTY_DECO: + return "Wet yellowish moss covers the floor."; + default: + return super.tileDesc( tile ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/SewerLevel.java b/src/com/watabou/pixeldungeon/levels/SewerLevel.java new file mode 100644 index 0000000000..e538effc66 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/SewerLevel.java @@ -0,0 +1,220 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Scene; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.mobs.npcs.Ghost; +import com.watabou.pixeldungeon.items.DewVial; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.ColorMath; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class SewerLevel extends RegularLevel { + + { + color1 = 0x48763c; + color2 = 0x59994a; + } + + @Override + public String tilesTex() { + return Assets.TILES_SEWERS; + } + + @Override + public String waterTex() { + return Assets.WATER_SEWERS; + } + + protected boolean[] water() { + return Patch.generate( feeling == Feeling.WATER ? 0.60f : 0.45f, 5 ); + } + + protected boolean[] grass() { + return Patch.generate( feeling == Feeling.GRASS ? 0.60f : 0.40f, 4 ); + } + + @Override + protected void decorate() { + + for (int i=0; i < WIDTH; i++) { + if (map[i] == Terrain.WALL && + map[i + WIDTH] == Terrain.WATER && + Random.Int( 4 ) == 0) { + + map[i] = Terrain.WALL_DECO; + } + } + + for (int i=WIDTH; i < LENGTH - WIDTH; i++) { + if (map[i] == Terrain.WALL && + map[i - WIDTH] == Terrain.WALL && + map[i + WIDTH] == Terrain.WATER && + Random.Int( 2 ) == 0) { + + map[i] = Terrain.WALL_DECO; + } + } + + for (int i=WIDTH + 1; i < LENGTH - WIDTH - 1; i++) { + if (map[i] == Terrain.EMPTY) { + + int count = + (map[i + 1] == Terrain.WALL ? 1 : 0) + + (map[i - 1] == Terrain.WALL ? 1 : 0) + + (map[i + WIDTH] == Terrain.WALL ? 1 : 0) + + (map[i - WIDTH] == Terrain.WALL ? 1 : 0); + + if (Random.Int( 16 ) < count * count) { + map[i] = Terrain.EMPTY_DECO; + } + } + } + + while (true) { + int pos = roomEntrance.random(); + if (pos != entrance) { + map[pos] = Terrain.SIGN; + break; + } + } + } + + @Override + protected void createMobs() { + super.createMobs(); + + Ghost.Quest.spawn( this ); + } + + @Override + protected void createItems() { + if (Dungeon.dewVial && Random.Int( 4 - Dungeon.depth ) == 0) { + addItemToSpawn( new DewVial() ); + Dungeon.dewVial = false; + } + + super.createItems(); + } + + @Override + public void addVisuals( Scene scene ) { + super.addVisuals( scene ); + addVisuals( this, scene ); + } + + public static void addVisuals( Level level, Scene scene ) { + for (int i=0; i < LENGTH; i++) { + if (level.map[i] == Terrain.WALL_DECO) { + scene.add( new Sink( i ) ); + } + } + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return "Murky water"; + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.EMPTY_DECO: + return "Wet yellowish moss covers the floor."; + case Terrain.BOOKSHELF: + return "The bookshelf is packed with cheap useless books. Might it burn?"; + default: + return super.tileDesc( tile ); + } + } + + private static class Sink extends Emitter { + + private int pos; + private float rippleDelay = 0; + + private static final Emitter.Factory factory = new Factory() { + + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + WaterParticle p = (WaterParticle)emitter.recycle( WaterParticle.class ); + p.reset( x, y ); + } + }; + + public Sink( int pos ) { + super(); + + this.pos = pos; + + PointF p = DungeonTilemap.tileCenterToWorld( pos ); + pos( p.x - 2, p.y + 1, 4, 0 ); + + pour( factory, 0.05f ); + } + + @Override + public void update() { + if (visible = Dungeon.visible[pos]) { + + super.update(); + + if ((rippleDelay -= Game.elapsed) <= 0) { + GameScene.ripple( pos + WIDTH ).y -= DungeonTilemap.SIZE / 2; + rippleDelay = Random.Float( 0.2f, 0.3f ); + } + } + } + } + + public static final class WaterParticle extends PixelParticle { + + public WaterParticle() { + super(); + + acc.y = 50; + am = 0.5f; + + color( ColorMath.random( 0xb6ccc2, 0x3b6653 ) ); + size( 2 ); + } + + public void reset( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + speed.set( Random.Float( -2, +2 ), 0 ); + + left = lifespan = 0.5f; + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/Terrain.java b/src/com/watabou/pixeldungeon/levels/Terrain.java new file mode 100644 index 0000000000..ee3b7e3eb3 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/Terrain.java @@ -0,0 +1,167 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels; + +public class Terrain { + + public static final int CHASM = 0; + public static final int EMPTY = 1; + public static final int GRASS = 2; + public static final int EMPTY_WELL = 3; + public static final int WALL = 4; + public static final int DOOR = 5; + public static final int OPEN_DOOR = 6; + public static final int ENTRANCE = 7; + public static final int EXIT = 8; + public static final int EMBERS = 9; + public static final int LOCKED_DOOR = 10; + public static final int PEDESTAL = 11; + public static final int WALL_DECO = 12; + public static final int BARRICADE = 13; + public static final int EMPTY_SP = 14; + public static final int HIGH_GRASS = 15; + public static final int EMPTY_DECO = 24; + public static final int LOCKED_EXIT = 25; + public static final int UNLOCKED_EXIT = 26; + public static final int SIGN = 29; + public static final int WELL = 34; + public static final int STATUE = 35; + public static final int STATUE_SP = 36; + public static final int BOOKSHELF = 41; + public static final int ALCHEMY = 42; + public static final int CHASM_FLOOR = 43; + public static final int CHASM_FLOOR_SP = 44; + public static final int CHASM_WALL = 45; + public static final int CHASM_WATER = 46; + + public static final int SECRET_DOOR = 16; + public static final int TOXIC_TRAP = 17; + public static final int SECRET_TOXIC_TRAP = 18; + public static final int FIRE_TRAP = 19; + public static final int SECRET_FIRE_TRAP = 20; + public static final int PARALYTIC_TRAP = 21; + public static final int SECRET_PARALYTIC_TRAP = 22; + public static final int INACTIVE_TRAP = 23; + public static final int POISON_TRAP = 27; + public static final int SECRET_POISON_TRAP = 28; + public static final int ALARM_TRAP = 30; + public static final int SECRET_ALARM_TRAP = 31; + public static final int LIGHTNING_TRAP = 32; + public static final int SECRET_LIGHTNING_TRAP = 33; + public static final int GRIPPING_TRAP = 37; + public static final int SECRET_GRIPPING_TRAP = 38; + public static final int SUMMONING_TRAP = 39; + public static final int SECRET_SUMMONING_TRAP = 40; + + public static final int WATER_TILES = 48; + public static final int WATER = 63; + + public static final int PASSABLE = 0x01; + public static final int LOS_BLOCKING = 0x02; + public static final int FLAMABLE = 0x04; + public static final int SECRET = 0x08; + public static final int SOLID = 0x10; + public static final int AVOID = 0x20; + public static final int LIQUID = 0x40; + public static final int PIT = 0x80; + + public static final int UNSTITCHABLE = 0x100; + + public static final int[] flags = new int[256]; + static { + flags[CHASM] = AVOID | PIT | UNSTITCHABLE; + flags[EMPTY] = PASSABLE; + flags[GRASS] = PASSABLE | FLAMABLE; + flags[EMPTY_WELL] = PASSABLE; + flags[WATER] = PASSABLE | LIQUID | UNSTITCHABLE; + flags[WALL] = LOS_BLOCKING | SOLID | UNSTITCHABLE; + flags[DOOR] = PASSABLE | LOS_BLOCKING | FLAMABLE | SOLID | UNSTITCHABLE; + flags[OPEN_DOOR] = PASSABLE | FLAMABLE | UNSTITCHABLE; + flags[ENTRANCE] = PASSABLE; + flags[EXIT] = PASSABLE; + flags[EMBERS] = PASSABLE; + flags[LOCKED_DOOR] = LOS_BLOCKING | SOLID | UNSTITCHABLE; + flags[PEDESTAL] = PASSABLE | UNSTITCHABLE; + flags[WALL_DECO] = flags[WALL]; + flags[BARRICADE] = FLAMABLE | SOLID | LOS_BLOCKING; + flags[EMPTY_SP] = flags[EMPTY] | UNSTITCHABLE; + flags[HIGH_GRASS] = PASSABLE | LOS_BLOCKING | FLAMABLE; + flags[EMPTY_DECO] = flags[EMPTY]; + flags[LOCKED_EXIT] = SOLID; + flags[UNLOCKED_EXIT]= PASSABLE; + flags[SIGN] = PASSABLE | FLAMABLE; + flags[WELL] = AVOID; + flags[STATUE] = SOLID; + flags[STATUE_SP] = flags[STATUE] | UNSTITCHABLE; + flags[BOOKSHELF] = flags[BARRICADE] | UNSTITCHABLE; + flags[ALCHEMY] = PASSABLE; + + flags[CHASM_WALL] = flags[CHASM]; + flags[CHASM_FLOOR] = flags[CHASM]; + flags[CHASM_FLOOR_SP] = flags[CHASM]; + flags[CHASM_WATER] = flags[CHASM]; + + flags[SECRET_DOOR] = flags[WALL] | SECRET | UNSTITCHABLE; + flags[TOXIC_TRAP] = AVOID; + flags[SECRET_TOXIC_TRAP] = flags[EMPTY] | SECRET; + flags[FIRE_TRAP] = AVOID; + flags[SECRET_FIRE_TRAP] = flags[EMPTY] | SECRET; + flags[PARALYTIC_TRAP] = AVOID; + flags[SECRET_PARALYTIC_TRAP] = flags[EMPTY] | SECRET; + flags[POISON_TRAP] = AVOID; + flags[SECRET_POISON_TRAP] = flags[EMPTY] | SECRET; + flags[ALARM_TRAP] = AVOID; + flags[SECRET_ALARM_TRAP] = flags[EMPTY] | SECRET; + flags[LIGHTNING_TRAP] = AVOID; + flags[SECRET_LIGHTNING_TRAP] = flags[EMPTY] | SECRET; + flags[GRIPPING_TRAP] = AVOID; + flags[SECRET_GRIPPING_TRAP] = flags[EMPTY] | SECRET; + flags[SUMMONING_TRAP] = AVOID; + flags[SECRET_SUMMONING_TRAP] = flags[EMPTY] | SECRET; + flags[INACTIVE_TRAP] = flags[EMPTY]; + + for (int i=WATER_TILES; i < WATER_TILES + 16; i++) { + flags[i] = flags[WATER]; + } + }; + + public static int discover( int terr ) { + switch (terr) { + case SECRET_DOOR: + return DOOR; + case SECRET_FIRE_TRAP: + return FIRE_TRAP; + case SECRET_PARALYTIC_TRAP: + return PARALYTIC_TRAP; + case SECRET_TOXIC_TRAP: + return TOXIC_TRAP; + case SECRET_POISON_TRAP: + return POISON_TRAP; + case SECRET_ALARM_TRAP: + return ALARM_TRAP; + case SECRET_LIGHTNING_TRAP: + return LIGHTNING_TRAP; + case SECRET_GRIPPING_TRAP: + return GRIPPING_TRAP; + case SECRET_SUMMONING_TRAP: + return SUMMONING_TRAP; + default: + return terr; + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/features/AlchemyPot.java b/src/com/watabou/pixeldungeon/levels/features/AlchemyPot.java new file mode 100644 index 0000000000..2d4fee824b --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/features/AlchemyPot.java @@ -0,0 +1,48 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.features; + +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.windows.WndBag; + +public class AlchemyPot { + + private static final String TXT_SELECT_SEED = "Select a seed to throw"; + + private static Hero hero; + private static int pos; + + public static void operate( Hero hero, int pos ) { + + AlchemyPot.hero = hero; + AlchemyPot.pos = pos; + + GameScene.selectItem( itemSelector, WndBag.Mode.SEED, TXT_SELECT_SEED ); + } + + private static final WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + item.cast( hero, pos ); + } + } + }; +} diff --git a/src/com/watabou/pixeldungeon/levels/features/Chasm.java b/src/com/watabou/pixeldungeon/levels/features/Chasm.java new file mode 100644 index 0000000000..57b892b2ec --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/features/Chasm.java @@ -0,0 +1,111 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.features; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.levels.RegularLevel; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.InterlevelScene; +import com.watabou.pixeldungeon.sprites.MobSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndOptions; +import com.watabou.utils.Random; + +public class Chasm { + + private static final String TXT_CHASM = "Chasm"; + private static final String TXT_YES = "Yes, I know what I'm doing"; + private static final String TXT_NO = "No, I changed my mind"; + private static final String TXT_JUMP = + "Do you really want to jump into the chasm? You can probably die."; + + public static boolean jumpConfirmed = false; + + public static void heroJump( final Hero hero ) { + GameScene.show( + new WndOptions( TXT_CHASM, TXT_JUMP, TXT_YES, TXT_NO ) { + @Override + protected void onSelect( int index ) { + if (index == 0) { + jumpConfirmed = true; + hero.resume(); + } + }; + } + ); + } + + public static void heroFall( int pos ) { + + jumpConfirmed = false; + + Sample.INSTANCE.play( Assets.SND_FALLING ); + + if (Dungeon.hero.isAlive()) { + Dungeon.hero.interrupt(); + InterlevelScene.mode = InterlevelScene.Mode.FALL; + if (Dungeon.level instanceof RegularLevel) { + Room room = ((RegularLevel)Dungeon.level).room( pos ); + InterlevelScene.fallIntoPit = room != null && room.type == Room.Type.WEAK_FLOOR; + } else { + InterlevelScene.fallIntoPit = false; + } + Game.switchScene( InterlevelScene.class ); + } else { + Dungeon.hero.sprite.visible = false; + } + } + + public static void heroLand() { + + Hero hero = Dungeon.hero; + + hero.sprite.burst( hero.sprite.blood(), 10 ); + Camera.main.shake( 4, 0.2f ); + + Buff.prolong( hero, Cripple.class, Cripple.DURATION ); + hero.damage( Random.IntRange( hero.HT / 3, hero.HT / 2 ), new Hero.Doom() { + @Override + public void onDeath() { + Badges.validateDeathFromFalling(); + + Dungeon.fail( Utils.format( ResultDescriptions.FALL, Dungeon.depth ) ); + GLog.n( "You fell to death..." ); + } + } ); + } + + public static void mobFall( Mob mob ) { + // Destroy instead of kill to prevent dropping loot + mob.destroy(); + + ((MobSprite)mob.sprite).fall(); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/features/Door.java b/src/com/watabou/pixeldungeon/levels/features/Door.java new file mode 100644 index 0000000000..29904910b2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/features/Door.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.features; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class Door { + + public static void enter( int pos ) { + Level.set( pos, Terrain.OPEN_DOOR ); + GameScene.updateMap( pos ); + Dungeon.observe(); + + if (Dungeon.visible[pos]) { + Sample.INSTANCE.play( Assets.SND_OPEN ); + } + } + + public static void leave( int pos ) { + if (Dungeon.level.heaps.get( pos ) == null) { + Level.set( pos, Terrain.DOOR ); + GameScene.updateMap( pos ); + Dungeon.observe(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/features/HighGrass.java b/src/com/watabou/pixeldungeon/levels/features/HighGrass.java new file mode 100644 index 0000000000..afb516c01b --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/features/HighGrass.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.features; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Barkskin; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.LeafParticle; +import com.watabou.pixeldungeon.items.Dewdrop; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.rings.RingOfHerbalism.Herbalism; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Random; + +public class HighGrass { + + public static void trample( Level level, int pos, Char ch ) { + + Level.set( pos, Terrain.GRASS ); + GameScene.updateMap( pos ); + + int herbalismLevel = 0; + if (ch != null) { + Herbalism herbalism = ch.buff( Herbalism.class ); + if (herbalism != null) { + herbalismLevel = herbalism.level; + } + } + + // Seed + if (herbalismLevel >= 0 && Random.Int( 18 ) <= Random.Int( herbalismLevel + 1 )) { + level.drop( Generator.random( Generator.Category.SEED ), pos ).sprite.drop(); + } + + // Dew + if (herbalismLevel >= 0 && Random.Int( 6 ) <= Random.Int( herbalismLevel + 1 )) { + level.drop( new Dewdrop(), pos ).sprite.drop(); + } + + int leaves = 4; + + // Barkskin + if (ch instanceof Hero && ((Hero)ch).subClass == HeroSubClass.WARDEN) { + Buff.affect( ch, Barkskin.class ).level( ch.HT / 3 ); + leaves = 8; + } + + CellEmitter.get( pos ).burst( LeafParticle.LEVEL_SPECIFIC, leaves ); + Dungeon.observe(); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/ArmoryPainter.java b/src/com/watabou/pixeldungeon/levels/painters/ArmoryPainter.java new file mode 100644 index 0000000000..542974fdb0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/ArmoryPainter.java @@ -0,0 +1,71 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class ArmoryPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + Room.Door entrance = room.entrance(); + Point statue = null; + if (entrance.x == room.left) { + statue = new Point( room.right-1, Random.Int( 2 ) == 0 ? room.top+1 : room.bottom-1 ); + } else if (entrance.x == room.right) { + statue = new Point( room.left+1, Random.Int( 2 ) == 0 ? room.top+1 : room.bottom-1 ); + } else if (entrance.y == room.top) { + statue = new Point( Random.Int( 2 ) == 0 ? room.left+1 : room.right-1, room.bottom-1 ); + } else if (entrance.y == room.bottom) { + statue = new Point( Random.Int( 2 ) == 0 ? room.left+1 : room.right-1, room.top+1 ); + } + if (statue != null) { + set( level, statue, Terrain.STATUE ); + } + + int n = Random.IntRange( 2, 3 ); + for (int i=0; i < n; i++) { + int pos; + do { + pos = room.random(); + } while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null); + level.drop( prize( level ), pos ); + } + + entrance.set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + } + + private static Item prize( Level level ) { + return Generator.random( Random.oneOf( + Generator.Category.ARMOR, + Generator.Category.WEAPON + ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/BlacksmithPainter.java b/src/com/watabou/pixeldungeon/levels/painters/BlacksmithPainter.java new file mode 100644 index 0000000000..ac98fc7cfc --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/BlacksmithPainter.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.npcs.Blacksmith; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class BlacksmithPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.FIRE_TRAP ); + fill( level, room, 2, Terrain.EMPTY_SP ); + + for (int i=0; i < 2; i++) { + int pos; + do { + pos = room.random(); + } while (level.map[pos] != Terrain.EMPTY_SP); + level.drop( + Generator.random( Random.oneOf( + Generator.Category.ARMOR, + Generator.Category.WEAPON + ) ), pos ); + } + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.UNLOCKED ); + drawInside( level, room, door, 1, Terrain.EMPTY ); + } + + Blacksmith npc = new Blacksmith(); + do { + npc.pos = room.random( 1 ); + } while (level.heaps.get( npc.pos ) != null); + level.mobs.add( npc ); + Actor.occupyCell( npc ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/BossExitPainter.java b/src/com/watabou/pixeldungeon/levels/painters/BossExitPainter.java new file mode 100644 index 0000000000..e2fa8be890 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/BossExitPainter.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; + +public class BossExitPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.REGULAR ); + } + + level.exit = room.top * Level.WIDTH + (room.left + room.right) / 2; + set( level, level.exit, Terrain.LOCKED_EXIT ); + } + +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/CryptPainter.java b/src/com/watabou/pixeldungeon/levels/painters/CryptPainter.java new file mode 100644 index 0000000000..cd26321f30 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/CryptPainter.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Heap.Type; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; + +public class CryptPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + Point c = room.center(); + int cx = c.x; + int cy = c.y; + + Room.Door entrance = room.entrance(); + + entrance.set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + + if (entrance.x == room.left) { + set( level, new Point( room.right-1, room.top+1 ), Terrain.STATUE ); + set( level, new Point( room.right-1, room.bottom-1 ), Terrain.STATUE ); + cx = room.right - 2; + } else if (entrance.x == room.right) { + set( level, new Point( room.left+1, room.top+1 ), Terrain.STATUE ); + set( level, new Point( room.left+1, room.bottom-1 ), Terrain.STATUE ); + cx = room.left + 2; + } else if (entrance.y == room.top) { + set( level, new Point( room.left+1, room.bottom-1 ), Terrain.STATUE ); + set( level, new Point( room.right-1, room.bottom-1 ), Terrain.STATUE ); + cy = room.bottom - 2; + } else if (entrance.y == room.bottom) { + set( level, new Point( room.left+1, room.top+1 ), Terrain.STATUE ); + set( level, new Point( room.right-1, room.top+1 ), Terrain.STATUE ); + cy = room.top + 2; + } + + level.drop( prize( level ), cx + cy * Level.WIDTH ).type = Type.TOMB; + } + + private static Item prize( Level level ) { + + Item prize = Generator.random( Generator.Category.ARMOR ); + + for (int i=0; i < 3; i++) { + Item another = Generator.random( Generator.Category.ARMOR ); + if (another.level > prize.level) { + prize = another; + } + } + + return prize; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/EntrancePainter.java b/src/com/watabou/pixeldungeon/levels/painters/EntrancePainter.java new file mode 100644 index 0000000000..8f89496922 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/EntrancePainter.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; + +public class EntrancePainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.REGULAR ); + } + + level.entrance = room.random( 1 ); + set( level, level.entrance, Terrain.ENTRANCE ); + } + +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/ExitPainter.java b/src/com/watabou/pixeldungeon/levels/painters/ExitPainter.java new file mode 100644 index 0000000000..ca9a96eaad --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/ExitPainter.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; + +public class ExitPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.REGULAR ); + } + + level.exit = room.random( 1 ); + set( level, level.exit, Terrain.EXIT ); + } + +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/GardenPainter.java b/src/com/watabou/pixeldungeon/levels/painters/GardenPainter.java new file mode 100644 index 0000000000..3189ed9da4 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/GardenPainter.java @@ -0,0 +1,53 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.actors.blobs.Foliage; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.plants.Sungrass; +import com.watabou.utils.Random; + +public class GardenPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.HIGH_GRASS ); + fill( level, room, 2, Terrain.GRASS ); + + room.entrance().set( Room.Door.Type.REGULAR ); + + int bushes = Random.Int( 3 ) == 0 ? (Random.Int( 5 ) == 0 ? 2 : 1) : 0; + for (int i=0; i < bushes; i++) { + level.plant( new Sungrass.Seed(), room.random() ); + } + + Foliage light = (Foliage)level.blobs.get( Foliage.class ); + if (light == null) { + light = new Foliage(); + } + for (int i=room.top + 1; i < room.bottom; i++) { + for (int j=room.left + 1; j < room.right; j++) { + light.seed( j + Level.WIDTH * i, 1 ); + } + } + level.blobs.put( Foliage.class, light ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/LaboratoryPainter.java b/src/com/watabou/pixeldungeon/levels/painters/LaboratoryPainter.java new file mode 100644 index 0000000000..e5e0f39409 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/LaboratoryPainter.java @@ -0,0 +1,83 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.blobs.Alchemy; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.items.potions.Potion; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class LaboratoryPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY_SP ); + + Room.Door entrance = room.entrance(); + + Point pot = null; + if (entrance.x == room.left) { + pot = new Point( room.right-1, Random.Int( 2 ) == 0 ? room.top + 1 : room.bottom - 1 ); + } else if (entrance.x == room.right) { + pot = new Point( room.left+1, Random.Int( 2 ) == 0 ? room.top + 1 : room.bottom - 1 ); + } else if (entrance.y == room.top) { + pot = new Point( Random.Int( 2 ) == 0 ? room.left + 1 : room.right - 1, room.bottom-1 ); + } else if (entrance.y == room.bottom) { + pot = new Point( Random.Int( 2 ) == 0 ? room.left + 1 : room.right - 1, room.top+1 ); + } + set( level, pot, Terrain.ALCHEMY ); + + Alchemy alchemy = new Alchemy(); + alchemy.seed( pot.x + Level.WIDTH * pot.y, 1 ); + level.blobs.put( Alchemy.class, alchemy ); + + int n = Random.IntRange( 2, 3 ); + for (int i=0; i < n; i++) { + int pos; + do { + pos = room.random(); + } while ( + level.map[pos] != Terrain.EMPTY_SP || + level.heaps.get( pos ) != null); + level.drop( prize( level ), pos ); + } + + entrance.set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + } + + private static Item prize( Level level ) { + + Item prize = level.itemToSpanAsPrize(); + if (prize instanceof Potion) { + return prize; + } else if (prize != null) { + level.addItemToSpawn( prize ); + } + + return Generator.random( Generator.Category.POTION ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/LibraryPainter.java b/src/com/watabou/pixeldungeon/levels/painters/LibraryPainter.java new file mode 100644 index 0000000000..5794992c7a --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/LibraryPainter.java @@ -0,0 +1,90 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class LibraryPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + Room.Door entrance = room.entrance(); + Point a = null; + Point b = null; + + if (entrance.x == room.left) { + a = new Point( room.left+1, entrance.y-1 ); + b = new Point( room.left+1, entrance.y+1 ); + fill( level, room.right - 1, room.top + 1, 1, room.height() - 1 , Terrain.BOOKSHELF ); + } else if (entrance.x == room.right) { + a = new Point( room.right-1, entrance.y-1 ); + b = new Point( room.right-1, entrance.y+1 ); + fill( level, room.left+1, room.top + 1, 1, room.height() - 1 , Terrain.BOOKSHELF ); + } else if (entrance.y == room.top) { + a = new Point( entrance.x+1, room.top+1 ); + b = new Point( entrance.x-1, room.top+1 ); + fill( level, room.left + 1, room.bottom-1, room.width() - 1, 1 , Terrain.BOOKSHELF ); + } else if (entrance.y == room.bottom) { + a = new Point( entrance.x+1, room.bottom-1 ); + b = new Point( entrance.x-1, room.bottom-1 ); + fill( level, room.left + 1, room.top+1, room.width() - 1, 1 , Terrain.BOOKSHELF ); + } + if (a != null && level.map[a.x + a.y * Level.WIDTH] == Terrain.EMPTY) { + set( level, a, Terrain.STATUE ); + } + if (b != null && level.map[b.x + b.y * Level.WIDTH] == Terrain.EMPTY) { + set( level, b, Terrain.STATUE ); + } + + int n = Random.IntRange( 2, 3 ); + for (int i=0; i < n; i++) { + int pos; + do { + pos = room.random(); + } while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null); + level.drop( prize( level), pos ); + } + + entrance.set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + } + + private static Item prize( Level level ) { + + Item prize = level.itemToSpanAsPrize(); + if (prize instanceof Scroll) { + return prize; + } else if (prize != null) { + level.addItemToSpawn( prize ); + } + + return Generator.random( Generator.Category.SCROLL ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/MagicWellPainter.java b/src/com/watabou/pixeldungeon/levels/painters/MagicWellPainter.java new file mode 100644 index 0000000000..aa83b49566 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/MagicWellPainter.java @@ -0,0 +1,67 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.blobs.WaterOfAwareness; +import com.watabou.pixeldungeon.actors.blobs.WaterOfHealth; +import com.watabou.pixeldungeon.actors.blobs.WaterOfTransmutation; +import com.watabou.pixeldungeon.actors.blobs.WellWater; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class MagicWellPainter extends Painter { + + private static final Class[] WATERS = + {WaterOfAwareness.class, WaterOfHealth.class, WaterOfTransmutation.class}; + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + Point c = room.center(); + set( level, c.x, c.y, Terrain.WELL ); + + @SuppressWarnings("unchecked") + Class waterClass = + Dungeon.depth >= Dungeon.transmutation ? + WaterOfTransmutation.class : + (Class)Random.element( WATERS ); + + if (waterClass == WaterOfTransmutation.class) { + Dungeon.transmutation = Integer.MAX_VALUE; + } + + WellWater water = (WellWater)level.blobs.get( waterClass ); + if (water == null) { + try { + water = waterClass.newInstance(); + } catch (Exception e) { + water = null; + } + } + water.seed( c.x + Level.WIDTH * c.y, 1 ); + level.blobs.put( waterClass, water ); + + room.entrance().set( Room.Door.Type.REGULAR ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/Painter.java b/src/com/watabou/pixeldungeon/levels/painters/Painter.java new file mode 100644 index 0000000000..cef7487b46 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/Painter.java @@ -0,0 +1,86 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import java.util.Arrays; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.utils.Point; +import com.watabou.utils.Rect; + +public class Painter { + + public static void set( Level level, int cell, int value ) { + level.map[cell] = value; + } + + public static void set( Level level, int x, int y, int value ) { + set( level, x + y * Level.WIDTH, value ); + } + + public static void set( Level level, Point p, int value ) { + set( level, p.x, p.y, value ); + } + + public static void fill( Level level, int x, int y, int w, int h, int value ) { + + int width = Level.WIDTH; + + int pos = y * width + x; + for (int i=y; i < y + h; i++, pos += width) { + Arrays.fill( level.map, pos, pos + w, value ); + } + } + + public static void fill( Level level, Rect rect, int value ) { + fill( level, rect.left, rect.top, rect.width() + 1, rect.height() + 1, value ); + } + + public static void fill( Level level, Rect rect, int m, int value ) { + fill( level, rect.left + m, rect.top + m, rect.width() + 1 - m*2, rect.height() + 1 - m*2, value ); + } + + public static void fill( Level level, Rect rect, int l, int t, int r, int b, int value ) { + fill( level, rect.left + l, rect.top + t, rect.width() + 1 - (l + r), rect.height() + 1 - (t + b), value ); + } + + public static Point drawInside( Level level, Room room, Point from, int n, int value ) { + + Point step = new Point(); + if (from.x == room.left) { + step.set( +1, 0 ); + } else if (from.x == room.right) { + step.set( -1, 0 ); + } else if (from.y == room.top) { + step.set( 0, +1 ); + } else if (from.y == room.bottom) { + step.set( 0, -1 ); + } + + Point p = new Point( from ).offset( step ); + for (int i=0; i < n; i++) { + if (value != -1) { + set( level, p, value ); + } + p.offset( step ); + } + + return p; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/PassagePainter.java b/src/com/watabou/pixeldungeon/levels/painters/PassagePainter.java new file mode 100644 index 0000000000..cc04549191 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/PassagePainter.java @@ -0,0 +1,116 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import java.util.ArrayList; +import java.util.Collections; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.utils.Point; + +public class PassagePainter extends Painter { + + private static int pasWidth; + private static int pasHeight; + + public static void paint( Level level, Room room ) { + + pasWidth = room.width() - 2; + pasHeight = room.height() - 2; + + int floor = level.tunnelTile(); + + ArrayList joints = new ArrayList(); + for (Point door : room.connected.values()) { + joints.add( xy2p( room, door ) ); + } + Collections.sort( joints ); + + int nJoints = joints.size(); + int perimeter = pasWidth * 2 + pasHeight * 2; + + int start = 0; + int maxD = joints.get( 0 ) + perimeter - joints.get( nJoints - 1 ); + for (int i=1; i < nJoints; i++) { + int d = joints.get( i ) - joints.get( i - 1 ); + if (d > maxD) { + maxD = d; + start = i; + } + } + + int end = (start + nJoints - 1) % nJoints; + + int p = joints.get( start ); + do { + set( level, p2xy( room, p ), floor ); + p = (p + 1) % perimeter; + } while (p != joints.get( end )); + + set( level, p2xy( room, p ), floor ); + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.TUNNEL ); + } + } + + private static int xy2p( Room room, Point xy ) { + if (xy.y == room.top) { + + return (xy.x - room.left - 1); + + } else if (xy.x == room.right) { + + return (xy.y - room.top - 1) + pasWidth; + + } else if (xy.y == room.bottom) { + + return (room.right - xy.x - 1) + pasWidth + pasHeight; + + } else { + + if (xy.y == room.top + 1) { + return 0; + } else { + return (room.bottom - xy.y - 1) + pasWidth * 2 + pasHeight; + } + + } + } + + private static Point p2xy( Room room, int p ) { + if (p < pasWidth) { + + return new Point( room.left + 1 + p, room.top + 1); + + } else if (p < pasWidth + pasHeight) { + + return new Point( room.right - 1, room.top + 1 + (p - pasWidth) ); + + } else if (p < pasWidth * 2 + pasHeight) { + + return new Point( room.right - 1 - (p - (pasWidth + pasHeight)), room.bottom - 1 ); + + } else { + + return new Point( room.left + 1, room.bottom - 1 - (p - (pasWidth * 2 + pasHeight)) ); + + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/PitPainter.java b/src/com/watabou/pixeldungeon/levels/painters/PitPainter.java new file mode 100644 index 0000000000..0ebbb0bdd0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/PitPainter.java @@ -0,0 +1,89 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Heap.Type; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class PitPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + Room.Door entrance = room.entrance(); + entrance.set( Room.Door.Type.LOCKED ); + + Point well = null; + if (entrance.x == room.left) { + well = new Point( room.right-1, Random.Int( 2 ) == 0 ? room.top + 1 : room.bottom - 1 ); + } else if (entrance.x == room.right) { + well = new Point( room.left+1, Random.Int( 2 ) == 0 ? room.top + 1 : room.bottom - 1 ); + } else if (entrance.y == room.top) { + well = new Point( Random.Int( 2 ) == 0 ? room.left + 1 : room.right - 1, room.bottom-1 ); + } else if (entrance.y == room.bottom) { + well = new Point( Random.Int( 2 ) == 0 ? room.left + 1 : room.right - 1, room.top+1 ); + } + set( level, well, Terrain.EMPTY_WELL ); + + int remains = room.random(); + while (level.map[remains] == Terrain.EMPTY_WELL) { + remains = room.random(); + } + + level.drop( new IronKey( Dungeon.depth ), remains ).type = Type.SKELETON; + + if (Random.Int( 5 ) == 0) { + level.drop( Generator.random( Generator.Category.RING ), remains ); + } else { + level.drop( Generator.random( Random.oneOf( + Generator.Category.WEAPON, + Generator.Category.ARMOR + ) ), remains ); + } + + int n = Random.IntRange( 1, 2 ); + for (int i=0; i < n; i++) { + level.drop( prize( level ), remains ); + } + } + + private static Item prize( Level level ) { + + Item prize = level.itemToSpanAsPrize(); + if (prize != null) { + return prize; + } + + return Generator.random( Random.oneOf( + Generator.Category.POTION, + Generator.Category.SCROLL, + Generator.Category.FOOD, + Generator.Category.GOLD + ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/PoolPainter.java b/src/com/watabou/pixeldungeon/levels/painters/PoolPainter.java new file mode 100644 index 0000000000..92a117d93c --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/PoolPainter.java @@ -0,0 +1,108 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.Piranha; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.potions.PotionOfInvisibility; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class PoolPainter extends Painter { + + private static final int NPIRANHAS = 3; + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.WATER ); + + Room.Door door = room.entrance(); + door.set( Room.Door.Type.REGULAR ); + + int x = -1; + int y = -1; + if (door.x == room.left) { + + x = room.right - 1; + y = room.top + room.height() / 2; + + } else if (door.x == room.right) { + + x = room.left + 1; + y = room.top + room.height() / 2; + + } else if (door.y == room.top) { + + x = room.left + room.width() / 2; + y = room.bottom - 1; + + } else if (door.y == room.bottom) { + + x = room.left + room.width() / 2; + y = room.top + 1; + + } + + int pos = x + y * Level.WIDTH; + level.drop( prize( level ), pos ).type = + Random.Int( 3 ) == 0 ? Heap.Type.CHEST : Heap.Type.HEAP; + set( level, pos, Terrain.PEDESTAL ); + + level.addItemToSpawn( new PotionOfInvisibility() ); + + for (int i=0; i < NPIRANHAS; i++) { + Piranha piranha = new Piranha(); + do { + piranha.pos = room.random(); + } while (level.map[piranha.pos] != Terrain.WATER|| Actor.findChar( piranha.pos ) != null); + level.mobs.add( piranha ); + Actor.occupyCell( piranha ); + } + } + + private static Item prize( Level level ) { + + Item prize = level.itemToSpanAsPrize(); + if (prize != null) { + return prize; + } + + prize = Generator.random( Random.oneOf( + Generator.Category.WEAPON, + Generator.Category.ARMOR + ) ); + + for (int i=0; i < 4; i++) { + Item another = Generator.random( Random.oneOf( + Generator.Category.WEAPON, + Generator.Category.ARMOR + ) ); + if (another.level > prize.level) { + prize = another; + } + } + + return prize; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/RatKingPainter.java b/src/com/watabou/pixeldungeon/levels/painters/RatKingPainter.java new file mode 100644 index 0000000000..3af1d78ef6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/RatKingPainter.java @@ -0,0 +1,86 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.actors.mobs.npcs.RatKing; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.weapon.missiles.MissileWeapon; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class RatKingPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY_SP ); + + Room.Door entrance = room.entrance(); + entrance.set( Room.Door.Type.HIDDEN ); + int door = entrance.x + entrance.y * Level.WIDTH; + + for (int i=room.left + 1; i < room.right; i++) { + addChest( level, (room.top + 1) * Level.WIDTH + i, door ); + addChest( level, (room.bottom - 1) * Level.WIDTH + i, door ); + } + + for (int i=room.top + 2; i < room.bottom - 1; i++) { + addChest( level, i * Level.WIDTH + room.left + 1, door ); + addChest( level, i * Level.WIDTH + room.right - 1, door ); + } + + RatKing king = new RatKing(); + king.pos = room.random( 1 ); + level.mobs.add( king ); + } + + private static void addChest( Level level, int pos, int door ) { + + if (pos == door - 1 || + pos == door + 1 || + pos == door - Level.WIDTH || + pos == door + Level.WIDTH) { + return; + } + + Item prize; + switch (Random.Int( 10 )) { + case 0: + prize = Generator.random( Generator.Category.WEAPON ); + if (prize instanceof MissileWeapon) { + prize.quantity( 1 ); + } else { + prize.degrade( Random.Int( 3 ) ); + } + break; + case 1: + prize = Generator.random( Generator.Category.ARMOR ).degrade( Random.Int( 3 ) ); + break; + default: + prize = new Gold( Random.IntRange( 1, 5 ) ); + break; + } + + level.drop( prize, pos ).type = Heap.Type.CHEST; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/ShopPainter.java b/src/com/watabou/pixeldungeon/levels/painters/ShopPainter.java new file mode 100644 index 0000000000..38b4997c59 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/ShopPainter.java @@ -0,0 +1,214 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.actors.mobs.npcs.ImpShopkeeper; +import com.watabou.pixeldungeon.actors.mobs.npcs.Shopkeeper; +import com.watabou.pixeldungeon.items.Ankh; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Torch; +import com.watabou.pixeldungeon.items.armor.*; +import com.watabou.pixeldungeon.items.bags.ScrollHolder; +import com.watabou.pixeldungeon.items.bags.SeedPouch; +import com.watabou.pixeldungeon.items.bags.WandHolster; +import com.watabou.pixeldungeon.items.food.OverpricedRation; +import com.watabou.pixeldungeon.items.potions.PotionOfHealing; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfIdentify; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfMagicMapping; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfRemoveCurse; +import com.watabou.pixeldungeon.items.weapon.melee.*; +import com.watabou.pixeldungeon.levels.LastShopLevel; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class ShopPainter extends Painter { + + private static int pasWidth; + private static int pasHeight; + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY_SP ); + + pasWidth = room.width() - 2; + pasHeight = room.height() - 2; + int per = pasWidth * 2 + pasHeight * 2; + + Item[] range = range(); + + int pos = xy2p( room, room.entrance() ) + (per - range.length) / 2; + for (int i=0; i < range.length; i++) { + + Point xy = p2xy( room, (pos + per) % per ); + int cell = xy.x + xy.y * Level.WIDTH; + + if (level.heaps.get( cell ) != null) { + do { + cell = room.random(); + } while (level.heaps.get( cell ) != null); + } + + level.drop( range[i], cell ).type = Heap.Type.FOR_SALE; + + pos++; + } + + placeShopkeeper( level, room ); + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.REGULAR ); + } + } + + private static Item[] range() { + + ArrayList items = new ArrayList(); + + switch (Dungeon.depth) { + + case 6: + items.add( (Random.Int( 2 ) == 0 ? new Quarterstaff() : new Spear()).identify() ); + items.add( new LeatherArmor().identify() ); + items.add( new SeedPouch() ); + break; + + case 11: + items.add( (Random.Int( 2 ) == 0 ? new Sword() : new Mace()).identify() ); + items.add( new MailArmor().identify() ); + items.add( new ScrollHolder() ); + break; + + case 16: + items.add( (Random.Int( 2 ) == 0 ? new Longsword() : new BattleAxe()).identify() ); + items.add( new ScaleArmor().identify() ); + items.add( new WandHolster() ); + break; + + case 21: + switch (Random.Int( 3 )) { + case 0: + items.add( new Glaive().identify() ); + break; + case 1: + items.add( new WarHammer().identify() ); + break; + case 2: + items.add( new PlateArmor().identify() ); + break; + } + items.add( new Torch() ); + items.add( new Torch() ); + break; + } + + items.add( new PotionOfHealing() ); + for (int i=0; i < 3; i++) { + items.add( Generator.random( Generator.Category.POTION ) ); + } + + items.add( new ScrollOfIdentify() ); + items.add( new ScrollOfRemoveCurse() ); + items.add( new ScrollOfMagicMapping() ); + items.add( Generator.random( Generator.Category.SCROLL ) ); + + items.add( new OverpricedRation() ); + items.add( new OverpricedRation() ); + + items.add( new Ankh() ); + + Item[] range =items.toArray( new Item[0] ); + Random.shuffle( range ); + + return range; + } + + private static void placeShopkeeper( Level level, Room room ) { + + int pos; + do { + pos = room.random(); + } while (level.heaps.get( pos ) != null); + + Mob shopkeeper = level instanceof LastShopLevel ? new ImpShopkeeper() : new Shopkeeper(); + shopkeeper.pos = pos; + level.mobs.add( shopkeeper ); + + if (level instanceof LastShopLevel) { + for (int i=0; i < Level.NEIGHBOURS9.length; i++) { + int p = shopkeeper.pos + Level.NEIGHBOURS9[i]; + if (level.map[p] == Terrain.EMPTY_SP) { + level.map[p] = Terrain.WATER; + } + } + } + } + + private static int xy2p( Room room, Point xy ) { + if (xy.y == room.top) { + + return (xy.x - room.left - 1); + + } else if (xy.x == room.right) { + + return (xy.y - room.top - 1) + pasWidth; + + } else if (xy.y == room.bottom) { + + return (room.right - xy.x - 1) + pasWidth + pasHeight; + + } else { + + if (xy.y == room.top + 1) { + return 0; + } else { + return (room.bottom - xy.y - 1) + pasWidth * 2 + pasHeight; + } + + } + } + + private static Point p2xy( Room room, int p ) { + if (p < pasWidth) { + + return new Point( room.left + 1 + p, room.top + 1); + + } else if (p < pasWidth + pasHeight) { + + return new Point( room.right - 1, room.top + 1 + (p - pasWidth) ); + + } else if (p < pasWidth * 2 + pasHeight) { + + return new Point( room.right - 1 - (p - (pasWidth + pasHeight)), room.bottom - 1 ); + + } else { + + return new Point( room.left + 1, room.bottom - 1 - (p - (pasWidth * 2 + pasHeight)) ); + + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/StandardPainter.java b/src/com/watabou/pixeldungeon/levels/painters/StandardPainter.java new file mode 100644 index 0000000000..e941a07683 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/StandardPainter.java @@ -0,0 +1,240 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class StandardPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.REGULAR ); + } + + if (!Dungeon.bossLevel() && Random.Int( 5 ) == 0) { + switch (Random.Int( 6 )) { + case 0: + if (level.feeling != Level.Feeling.GRASS) { + if (Math.min( room.width(), room.height() ) >= 4 && Math.max( room.width(), room.height() ) >= 6) { + paintGraveyard( level, room ); + return; + } + break; + } else { + // Burned room + } + case 1: + if (Dungeon.depth > 1) { + paintBurned( level, room ); + return; + } + break; + case 2: + if (Math.max( room.width(), room.height() ) >= 4) { + paintStriped( level, room ); + return; + } + break; + case 3: + if (room.width() >= 6 && room.height() >= 6) { + paintStudy( level, room ); + return; + } + break; + case 4: + if (level.feeling != Level.Feeling.WATER) { + if (room.connected.size() == 2 && room.width() >= 4 && room.height() >= 4) { + paintBridge( level, room ); + return; + } + break; + } else { + // Fissure + } + case 5: + if (!Dungeon.bossLevel() && !Dungeon.bossLevel( Dungeon.depth + 1 ) && + Math.min( room.width(), room.height() ) >= 5) { + paintFissure( level, room ); + return; + } + break; + } + } + + fill( level, room, 1, Terrain.EMPTY ); + } + + private static void paintBurned( Level level, Room room ) { + for (int i=room.top + 1; i < room.bottom; i++) { + for (int j=room.left + 1; j < room.right; j++) { + int t = Terrain.EMBERS; + switch (Random.Int( 5 )) { + case 0: + t = Terrain.EMPTY; + break; + case 1: + t = Terrain.FIRE_TRAP; + break; + case 2: + t = Terrain.SECRET_FIRE_TRAP; + break; + case 3: + t = Terrain.INACTIVE_TRAP; + break; + } + level.map[i * Level.WIDTH + j] = t; + } + } + } + + private static void paintGraveyard( Level level, Room room ) { + fill( level, room.left + 1, room.top + 1, room.width() - 1, room.height() - 1 , Terrain.GRASS ); + + int w = room.width() - 1; + int h = room.height() - 1; + int nGraves = Math.max( w, h ) / 2; + + int index = Random.Int( nGraves ); + + int shift = Random.Int( 2 ); + for (int i=0; i < nGraves; i++) { + int pos = w > h ? + room.left + 1 + shift + i * 2 + (room.top + 2 + Random.Int( h-2 )) * Level.WIDTH : + (room.left + 2 + Random.Int( w-2 )) + (room.top + 1 + shift + i * 2) * Level.WIDTH; + level.drop( i == index ? Generator.random() : new Gold(), pos ).type = Heap.Type.TOMB; + } + } + + private static void paintStriped( Level level, Room room ) { + fill( level, room.left + 1, room.top + 1, room.width() - 1, room.height() - 1 , Terrain.EMPTY_SP ); + + if (room.width() > room.height()) { + for (int i=room.left + 2; i < room.right; i += 2) { + fill( level, i, room.top + 1, 1, room.height() - 1, Terrain.HIGH_GRASS ); + } + } else { + for (int i=room.top + 2; i < room.bottom; i += 2) { + fill( level, room.left + 1, i, room.width() - 1, 1, Terrain.HIGH_GRASS ); + } + } + } + + private static void paintStudy( Level level, Room room ) { + fill( level, room.left + 1, room.top + 1, room.width() - 1, room.height() - 1 , Terrain.BOOKSHELF ); + fill( level, room.left + 2, room.top + 2, room.width() - 3, room.height() - 3 , Terrain.EMPTY_SP ); + + for (Point door : room.connected.values()) { + if (door.x == room.left) { + set( level, door.x + 1, door.y, Terrain.EMPTY ); + } else if (door.x == room.right) { + set( level, door.x - 1, door.y, Terrain.EMPTY ); + } else if (door.y == room.top) { + set( level, door.x, door.y + 1, Terrain.EMPTY ); + } else if (door.y == room.bottom) { + set( level, door.x , door.y - 1, Terrain.EMPTY ); + } + } + + set( level, room.center(), Terrain.PEDESTAL ); + } + + private static void paintBridge( Level level, Room room ) { + + fill( level, room.left + 1, room.top + 1, room.width() - 1, room.height() - 1 , + !Dungeon.bossLevel() && !Dungeon.bossLevel( Dungeon.depth + 1 ) && Random.Int( 3 ) == 0 ? + Terrain.CHASM : + Terrain.WATER ); + + Point door1 = null; + Point door2 = null; + for (Point p : room.connected.values()) { + if (door1 == null) { + door1 = p; + } else { + door2 = p; + } + } + + if ((door1.x == room.left && door2.x == room.right) || + (door1.x == room.right && door2.x == room.left)) { + + int s = room.width() / 2; + + drawInside( level, room, door1, s, Terrain.EMPTY_SP ); + drawInside( level, room, door2, s, Terrain.EMPTY_SP ); + fill( level, room.center().x, Math.min( door1.y, door2.y ), 1, Math.abs( door1.y - door2.y ) + 1, Terrain.EMPTY_SP ); + + } else + if ((door1.y == room.top && door2.y == room.bottom) || + (door1.y == room.bottom && door2.y == room.top)) { + + int s = room.height() / 2; + + drawInside( level, room, door1, s, Terrain.EMPTY_SP ); + drawInside( level, room, door2, s, Terrain.EMPTY_SP ); + fill( level, Math.min( door1.x, door2.x ), room.center().y, Math.abs( door1.x - door2.x ) + 1, 1, Terrain.EMPTY_SP ); + + } else + if (door1.x == door2.x) { + + fill( level, door1.x == room.left ? room.left + 1 : room.right - 1, Math.min( door1.y, door2.y ), 1, Math.abs( door1.y - door2.y ) + 1, Terrain.EMPTY_SP ); + + } else + if (door1.y == door2.y) { + + fill( level, Math.min( door1.x, door2.x ), door1.y == room.top ? room.top + 1 : room.bottom - 1, Math.abs( door1.x - door2.x ) + 1, 1, Terrain.EMPTY_SP ); + + } else + if (door1.y == room.top || door1.y == room.bottom) { + + drawInside( level, room, door1, Math.abs( door1.y - door2.y ), Terrain.EMPTY_SP ); + drawInside( level, room, door2, Math.abs( door1.x - door2.x ), Terrain.EMPTY_SP ); + + } else + if (door1.x == room.left || door1.x == room.right) { + + drawInside( level, room, door1, Math.abs( door1.x - door2.x ), Terrain.EMPTY_SP ); + drawInside( level, room, door2, Math.abs( door1.y - door2.y ), Terrain.EMPTY_SP ); + + } + } + + private static void paintFissure( Level level, Room room ) { + fill( level, room.left + 1, room.top + 1, room.width() - 1, room.height() - 1 ,Terrain.EMPTY ); + + for (int i=room.top + 2; i < room.bottom - 1; i++) { + for (int j=room.left + 2; j < room.right - 1; j++) { + int v = Math.min( i - room.top, room.bottom - i ); + int h = Math.min( j - room.left, room.right - j ); + if (Math.min( v, h ) > 2 || Random.Int( 2 ) == 0) { + set( level, j, i, Terrain.CHASM ); + } + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/StatuePainter.java b/src/com/watabou/pixeldungeon/levels/painters/StatuePainter.java new file mode 100644 index 0000000000..0f4af20ac3 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/StatuePainter.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.Statue; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; + +public class StatuePainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + Point c = room.center(); + int cx = c.x; + int cy = c.y; + + Room.Door door = room.entrance(); + + door.set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + + if (door.x == room.left) { + + fill( level, room.right - 1, room.top + 1, 1, room.height() - 1 , Terrain.STATUE ); + cx = room.right - 2; + + } else if (door.x == room.right) { + + fill( level, room.left + 1, room.top + 1, 1, room.height() - 1 , Terrain.STATUE ); + cx = room.left + 2; + + } else if (door.y == room.top) { + + fill( level, room.left + 1, room.bottom - 1, room.width() - 1, 1 , Terrain.STATUE ); + cy = room.bottom - 2; + + } else if (door.y == room.bottom) { + + fill( level, room.left + 1, room.top + 1, room.width() - 1, 1 , Terrain.STATUE ); + cy = room.top + 2; + + } + + Statue statue = new Statue(); + statue.pos = cx + cy * Level.WIDTH; + level.mobs.add( statue ); + Actor.occupyCell( statue ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/StoragePainter.java b/src/com/watabou/pixeldungeon/levels/painters/StoragePainter.java new file mode 100644 index 0000000000..3cdd40829a --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/StoragePainter.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.potions.PotionOfLiquidFlame; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class StoragePainter extends Painter { + + public static void paint( Level level, Room room ) { + + final int floor = Terrain.EMPTY_SP; + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, floor ); + + int n = Random.IntRange( 3, 4 ); + for (int i=0; i < n; i++) { + int pos; + do { + pos = room.random(); + } while (level.map[pos] != floor); + level.drop( prize( level ), pos ); + } + + room.entrance().set( Room.Door.Type.BARRICADE ); + level.addItemToSpawn( new PotionOfLiquidFlame() ); + } + + private static Item prize( Level level ) { + + Item prize = level.itemToSpanAsPrize(); + if (prize != null) { + return prize; + } + + return Generator.random( Random.oneOf( + Generator.Category.POTION, + Generator.Category.SCROLL, + Generator.Category.FOOD, + Generator.Category.GOLD + ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/TrapsPainter.java b/src/com/watabou/pixeldungeon/levels/painters/TrapsPainter.java new file mode 100644 index 0000000000..45ba75fabd --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/TrapsPainter.java @@ -0,0 +1,104 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.potions.PotionOfLevitation; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class TrapsPainter extends Painter { + + public static void paint( Level level, Room room ) { + + Integer traps[] = { + Terrain.TOXIC_TRAP, Terrain.TOXIC_TRAP, Terrain.TOXIC_TRAP, + Terrain.PARALYTIC_TRAP, Terrain.PARALYTIC_TRAP, + !Dungeon.bossLevel( Dungeon.depth + 1 ) ? Terrain.CHASM : Terrain.SUMMONING_TRAP }; + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Random.element( traps ) ); + + Room.Door door = room.entrance(); + door.set( Room.Door.Type.REGULAR ); + + int lastRow = level.map[room.left + 1 + (room.top + 1) * Level.WIDTH] == Terrain.CHASM ? Terrain.CHASM : Terrain.EMPTY; + + int x = -1; + int y = -1; + if (door.x == room.left) { + x = room.right - 1; + y = room.top + room.height() / 2; + fill( level, x, room.top + 1, 1, room.height() - 1 , lastRow ); + } else if (door.x == room.right) { + x = room.left + 1; + y = room.top + room.height() / 2; + fill( level, x, room.top + 1, 1, room.height() - 1 , lastRow ); + } else if (door.y == room.top) { + x = room.left + room.width() / 2; + y = room.bottom - 1; + fill( level, room.left + 1, y, room.width() - 1, 1 , lastRow ); + } else if (door.y == room.bottom) { + x = room.left + room.width() / 2; + y = room.top + 1; + fill( level, room.left + 1, y, room.width() - 1, 1 , lastRow ); + } + + int pos = x + y * Level.WIDTH; + if (Random.Int( 3 ) == 0) { + if (lastRow == Terrain.CHASM) { + set( level, pos, Terrain.EMPTY ); + } + level.drop( prize( level ), pos ).type = Heap.Type.CHEST; + } else { + set( level, pos, Terrain.PEDESTAL ); + level.drop( prize( level ), pos ); + } + + level.addItemToSpawn( new PotionOfLevitation() ); + } + + private static Item prize( Level level ) { + + Item prize = level.itemToSpanAsPrize(); + if (prize != null) { + return prize; + } + + prize = Generator.random( Random.oneOf( + Generator.Category.WEAPON, + Generator.Category.ARMOR + ) ); + + for (int i=0; i < 3; i++) { + Item another = Generator.random( Random.oneOf( + Generator.Category.WEAPON, + Generator.Category.ARMOR + ) ); + if (another.level > prize.level) { + prize = another; + } + } + + return prize; + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/TreasuryPainter.java b/src/com/watabou/pixeldungeon/levels/painters/TreasuryPainter.java new file mode 100644 index 0000000000..ea3c6163c3 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/TreasuryPainter.java @@ -0,0 +1,62 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class TreasuryPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + set( level, room.center(), Terrain.STATUE ); + + Heap.Type heapType = Random.Int( 2 ) == 0 ? Heap.Type.CHEST : Heap.Type.HEAP; + + int n = Random.IntRange( 2, 3 ); + for (int i=0; i < n; i++) { + int pos; + do { + pos = room.random(); + } while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null); + level.drop( new Gold().random(), pos ).type = heapType; + } + + if (heapType == Heap.Type.HEAP) { + for (int i=0; i < 6; i++) { + int pos; + do { + pos = room.random(); + } while (level.map[pos] != Terrain.EMPTY); + level.drop( new Gold( Random.IntRange( 1, 3 ) ), pos ); + } + } + + room.entrance().set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/TunnelPainter.java b/src/com/watabou/pixeldungeon/levels/painters/TunnelPainter.java new file mode 100644 index 0000000000..2544f777e9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/TunnelPainter.java @@ -0,0 +1,120 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class TunnelPainter extends Painter { + + public static void paint( Level level, Room room ) { + + int floor = level.tunnelTile(); + + Point c = room.center(); + + if (room.width() > room.height() || (room.width() == room.height() && Random.Int( 2 ) == 0)) { + + int from = room.right - 1; + int to = room.left + 1; + + for (Room.Door door : room.connected.values()) { + + int step = door.y < c.y ? +1 : -1; + + if (door.x == room.left) { + + from = room.left + 1; + for (int i=door.y; i != c.y; i += step) { + set( level, from, i, floor ); + } + + } else if (door.x == room.right) { + + to = room.right - 1; + for (int i=door.y; i != c.y; i += step) { + set( level, to, i, floor ); + } + + } else { + if (door.x < from) { + from = door.x; + } + if (door.x > to) { + to = door.x; + } + + for (int i=door.y+step; i != c.y; i += step) { + set( level, door.x, i, floor ); + } + } + } + + for (int i=from; i <= to; i++) { + set( level, i, c.y, floor ); + } + + } else { + + int from = room.bottom - 1; + int to = room.top + 1; + + for (Room.Door door : room.connected.values()) { + + int step = door.x < c.x ? +1 : -1; + + if (door.y == room.top) { + + from = room.top + 1; + for (int i=door.x; i != c.x; i += step) { + set( level, i, from, floor ); + } + + } else if (door.y == room.bottom) { + + to = room.bottom - 1; + for (int i=door.x; i != c.x; i += step) { + set( level, i, to, floor ); + } + + } else { + if (door.y < from) { + from = door.y; + } + if (door.y > to) { + to = door.y; + } + + for (int i=door.x+step; i != c.x; i += step) { + set( level, i, door.y, floor ); + } + } + } + + for (int i=from; i <= to; i++) { + set( level, c.x, i, floor ); + } + } + + for (Room.Door door : room.connected.values()) { + door.set( Room.Door.Type.TUNNEL ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/VaultPainter.java b/src/com/watabou/pixeldungeon/levels/painters/VaultPainter.java new file mode 100644 index 0000000000..9de626b698 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/VaultPainter.java @@ -0,0 +1,76 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.Heap.Type; +import com.watabou.pixeldungeon.items.keys.GoldenKey; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Random; + +public class VaultPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.EMPTY ); + + int cx = (room.left + room.right) / 2; + int cy = (room.top + room.bottom) / 2; + int c = cx + cy * Level.WIDTH; + + switch (Random.Int( 3 )) { + + case 0: + level.drop( prize( level ), c ).type = Type.LOCKED_CHEST; + level.addItemToSpawn( new GoldenKey( Dungeon.depth ) ); + break; + + case 1: + Item i1, i2; + do { + i1 = prize( level ); + i2 = prize( level ); + } while (i1.getClass() == i2.getClass()); + level.drop( i1, c ).type = Type.CRYSTAL_CHEST; + level.drop( i2, c + Level.NEIGHBOURS8[Random.Int( 8 )]).type = Type.CRYSTAL_CHEST; + level.addItemToSpawn( new GoldenKey( Dungeon.depth ) ); + break; + + case 2: + level.drop( prize( level ), c ); + set( level, c, Terrain.PEDESTAL ); + break; + } + + room.entrance().set( Room.Door.Type.LOCKED ); + level.addItemToSpawn( new IronKey( Dungeon.depth ) ); + } + + private static Item prize( Level level ) { + return Generator.random( Random.oneOf( + Generator.Category.WAND, + Generator.Category.RING + ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/painters/WeakFloorPainter.java b/src/com/watabou/pixeldungeon/levels/painters/WeakFloorPainter.java new file mode 100644 index 0000000000..5da8bd3d12 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/painters/WeakFloorPainter.java @@ -0,0 +1,54 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.painters; + +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Room; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class WeakFloorPainter extends Painter { + + public static void paint( Level level, Room room ) { + + fill( level, room, Terrain.WALL ); + fill( level, room, 1, Terrain.CHASM ); + + Room.Door door = room.entrance(); + door.set( Room.Door.Type.REGULAR ); + + if (door.x == room.left) { + for (int i=room.top + 1; i < room.bottom; i++) { + drawInside( level, room, new Point( room.left, i ), Random.IntRange( 1, room.width() - 2 ), Terrain.EMPTY_SP ); + } + } else if (door.x == room.right) { + for (int i=room.top + 1; i < room.bottom; i++) { + drawInside( level, room, new Point( room.right, i ), Random.IntRange( 1, room.width() - 2 ), Terrain.EMPTY_SP ); + } + } else if (door.y == room.top) { + for (int i=room.left + 1; i < room.right; i++) { + drawInside( level, room, new Point( i, room.top ), Random.IntRange( 1, room.height() - 2 ), Terrain.EMPTY_SP ); + } + } else if (door.y == room.bottom) { + for (int i=room.left + 1; i < room.right; i++) { + drawInside( level, room, new Point( i, room.bottom ), Random.IntRange( 1, room.height() - 2 ), Terrain.EMPTY_SP ); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/AlarmTrap.java b/src/com/watabou/pixeldungeon/levels/traps/AlarmTrap.java new file mode 100644 index 0000000000..7eeca71aaf --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/AlarmTrap.java @@ -0,0 +1,48 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.utils.GLog; + +public class AlarmTrap { + + // 0xDD3333 + + public static void trigger( int pos, Char ch ) { + + for (Mob mob : Dungeon.level.mobs) { + if (mob != ch) { + mob.beckon( pos ); + } + } + + if (Dungeon.visible[pos]) { + GLog.w( "The trap emits a piercing sound that echoes throughout the dungeon!" ); + CellEmitter.center( pos ).start( Speck.factory( Speck.SCREAM ), 0.3f, 3 ); + } + + Sample.INSTANCE.play( Assets.SND_ALERT ); + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/FireTrap.java b/src/com/watabou/pixeldungeon/levels/traps/FireTrap.java new file mode 100644 index 0000000000..64d638d368 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/FireTrap.java @@ -0,0 +1,37 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class FireTrap { + + // 0xFF7708 + + public static void trigger( int pos, Char ch ) { + + GameScene.add( Blob.seed( pos, 2, Fire.class ) ); + CellEmitter.get( pos ).burst( FlameParticle.FACTORY, 5 ); + + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/GrippingTrap.java b/src/com/watabou/pixeldungeon/levels/traps/GrippingTrap.java new file mode 100644 index 0000000000..7e5878ed02 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/GrippingTrap.java @@ -0,0 +1,42 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Bleeding; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Cripple; +import com.watabou.pixeldungeon.effects.Wound; +import com.watabou.utils.Random; + +public class GrippingTrap { + + public static void trigger( int pos, Char c ) { + + if (c != null) { + int damage = Math.max( 0, (Dungeon.depth + 3) - Random.IntRange( 0, c.dr() / 2 ) ); + Buff.affect( c, Bleeding.class ).set( damage ); + Buff.prolong( c, Cripple.class, Cripple.DURATION ); + Wound.hit( c ); + } else { + Wound.hit( pos ); + } + + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/LightningTrap.java b/src/com/watabou/pixeldungeon/levels/traps/LightningTrap.java new file mode 100644 index 0000000000..d1b16a5300 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/LightningTrap.java @@ -0,0 +1,73 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.ResultDescriptions; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Lightning; +import com.watabou.pixeldungeon.effects.particles.SparkParticle; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Random; + +public class LightningTrap { + + private static final String name = "lightning trap"; + + // 00x66CCEE + + public static void trigger( int pos, Char ch ) { + + if (ch != null) { + ch.damage( Math.max( 1, Random.Int( ch.HP / 3, 2 * ch.HP / 3 ) ), LIGHTNING ); + if (ch == Dungeon.hero) { + + Camera.main.shake( 2, 0.3f ); + + if (!ch.isAlive()) { + Dungeon.fail( Utils.format( ResultDescriptions.TRAP, name, Dungeon.depth ) ); + GLog.n( "You were killed by a discharge of a lightning trap..." ); + } else { + ((Hero)ch).belongings.charge( false ); + } + } + + int[] points = new int[2]; + + points[0] = pos - Level.WIDTH; + points[1] = pos + Level.WIDTH; + ch.sprite.parent.add( new Lightning( points, 2, null ) ); + + points[0] = pos - 1; + points[1] = pos + 1; + ch.sprite.parent.add( new Lightning( points, 2, null ) ); + } + + CellEmitter.center( pos ).burst( SparkParticle.FACTORY, Random.IntRange( 3, 4 ) ); + + } + + public static final Electricity LIGHTNING = new Electricity(); + public static class Electricity { + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/ParalyticTrap.java b/src/com/watabou/pixeldungeon/levels/traps/ParalyticTrap.java new file mode 100644 index 0000000000..a858fef6ec --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/ParalyticTrap.java @@ -0,0 +1,35 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ParalyticGas; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class ParalyticTrap { + + // 0xCCCC55 + + public static void trigger( int pos, Char ch ) { + + GameScene.add( Blob.seed( pos, 80 + 5 * Dungeon.depth, ParalyticGas.class ) ); + + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/PoisonTrap.java b/src/com/watabou/pixeldungeon/levels/traps/PoisonTrap.java new file mode 100644 index 0000000000..50a109d22c --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/PoisonTrap.java @@ -0,0 +1,40 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.PoisonParticle; + +public class PoisonTrap { + + // 0xBB66EE + + public static void trigger( int pos, Char ch ) { + + if (ch != null) { + Buff.affect( ch, Poison.class ).set( 5 + Math.min( Dungeon.depth, 15 ) ); + } + + CellEmitter.center( pos ).burst( PoisonParticle.SPLASH, 3 ); + + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/SummoningTrap.java b/src/com/watabou/pixeldungeon/levels/traps/SummoningTrap.java new file mode 100644 index 0000000000..ba9e678b0b --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/SummoningTrap.java @@ -0,0 +1,88 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Bestiary; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Random; + +public class SummoningTrap { + + private static final float DELAY = 2f; + + private static final Mob DUMMY = new Mob() {}; + + // 0x770088 + + public static void trigger( int pos, Char c ) { + + if (Dungeon.bossLevel()) { + return; + } + + if (c != null) { + Actor.occupyCell( c ); + } + + int nMobs = 1; + if (Random.Int( 2 ) == 0) { + nMobs++; + if (Random.Int( 2 ) == 0) { + nMobs++; + } + } + + // It's complicated here, because these traps can be activated in chain + + ArrayList candidates = new ArrayList(); + + for (int i=0; i < Level.NEIGHBOURS8.length; i++) { + int p = pos + Level.NEIGHBOURS8[i]; + if (Actor.findChar( p ) == null && (Level.passable[p] || Level.avoid[p])) { + candidates.add( p ); + } + } + + ArrayList respawnPoints = new ArrayList(); + + while (nMobs > 0 && candidates.size() > 0) { + int index = Random.index( candidates ); + + DUMMY.pos = candidates.get( index ); + Actor.occupyCell( DUMMY ); + + respawnPoints.add( candidates.remove( index ) ); + nMobs--; + } + + for (Integer point : respawnPoints) { + Mob mob = Bestiary.mob( Dungeon.depth ); + mob.state = Mob.State.WANDERING; + GameScene.add( mob, DELAY ); + WandOfBlink.appear( mob, point ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/levels/traps/ToxicTrap.java b/src/com/watabou/pixeldungeon/levels/traps/ToxicTrap.java new file mode 100644 index 0000000000..61957c59f7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/levels/traps/ToxicTrap.java @@ -0,0 +1,35 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.levels.traps; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.ToxicGas; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class ToxicTrap { + + // 0x40CC55 + + public static void trigger( int pos, Char ch ) { + + GameScene.add( Blob.seed( pos, 300 + 20 * Dungeon.depth, ToxicGas.class ) ); + + } +} diff --git a/src/com/watabou/pixeldungeon/mechanics/Ballistica.java b/src/com/watabou/pixeldungeon/mechanics/Ballistica.java new file mode 100644 index 0000000000..4f71a1f2ab --- /dev/null +++ b/src/com/watabou/pixeldungeon/mechanics/Ballistica.java @@ -0,0 +1,98 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.mechanics; + +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.levels.Level; + +public class Ballistica { + + public static int[] trace = new int[Math.max( Level.WIDTH, Level.HEIGHT )]; + public static int distance; + + public static int cast( int from, int to, boolean magic, boolean hitChars ) { + + int w = Level.WIDTH; + + int x0 = from % w; + int x1 = to % w; + int y0 = from / w; + int y1 = to / w; + + int dx = x1 - x0; + int dy = y1 - y0; + + int stepX = dx > 0 ? +1 : -1; + int stepY = dy > 0 ? +1 : -1; + + dx = Math.abs( dx ); + dy = Math.abs( dy ); + + int stepA; + int stepB; + int dA; + int dB; + + if (dx > dy) { + + stepA = stepX; + stepB = stepY * w; + dA = dx; + dB = dy; + + } else { + + stepA = stepY * w; + stepB = stepX; + dA = dy; + dB = dx; + + } + + distance = 1; + trace[0] = from; + + int cell = from; + + int err = dA / 2; + while (cell != to || magic) { + + cell += stepA; + + err += dB; + if (err >= dA) { + err = err - dA; + cell = cell + stepB; + } + + trace[distance++] = cell; + + if (!Level.passable[cell] && !Level.avoid[cell]) { + return trace[--distance - 1]; + } + + if (Level.losBlocking[cell] || (hitChars && Actor.findChar( cell ) != null)) { + return cell; + } + } + + trace[distance++] = cell; + + return to; + } +} diff --git a/src/com/watabou/pixeldungeon/mechanics/ShadowCaster.java b/src/com/watabou/pixeldungeon/mechanics/ShadowCaster.java new file mode 100644 index 0000000000..696bd7a243 --- /dev/null +++ b/src/com/watabou/pixeldungeon/mechanics/ShadowCaster.java @@ -0,0 +1,152 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.mechanics; + +import java.util.Arrays; + +import com.watabou.pixeldungeon.levels.Level; + +public final class ShadowCaster { + + private static final int MAX_DISTANCE = 8; + + private static final int WIDTH = Level.WIDTH; + private static final int HEIGHT = Level.HEIGHT; + + private static int distance; + private static int limits[]; + + private static boolean[] losBlocking; + private static boolean[] fieldOfView; + + private static int[][] rounding; + static { + rounding = new int[MAX_DISTANCE+1][]; + for (int i=1; i <= MAX_DISTANCE; i++) { + rounding[i] = new int[i+1]; + for (int j=1; j <= i; j++) { + rounding[i][j] = (int)Math.min( j, Math.round( i * Math.cos( Math.asin( j / (i + 0.5) )))); + } + } + } + + private static Obstacles obs = new Obstacles(); + + public static void castShadow( int x, int y, boolean[] fieldOfView, int distance ) { + + losBlocking = Level.losBlocking; + + ShadowCaster.distance = distance; + limits = rounding[distance]; + + ShadowCaster.fieldOfView = fieldOfView; + Arrays.fill( fieldOfView, false ); + fieldOfView[y * WIDTH + x] = true; + + scanSector( x, y, +1, +1, 0, 0 ); + scanSector( x, y, -1, +1, 0, 0 ); + scanSector( x, y, +1, -1, 0, 0 ); + scanSector( x, y, -1, -1, 0, 0 ); + scanSector( x, y, 0, 0, +1, +1 ); + scanSector( x, y, 0, 0, -1, +1 ); + scanSector( x, y, 0, 0, +1, -1 ); + scanSector( x, y, 0, 0, -1, -1 ); + } + + private static void scanSector( int cx, int cy, int m1, int m2, int m3, int m4 ) { + + obs.reset(); + + for (int p=1; p <= distance; p++) { + + float dq2 = 0.5f / p; + + int pp = limits[p]; + for (int q=0; q <= pp; q++) { + + int x = cx + q * m1 + p * m3; + int y = cy + p * m2 + q * m4; + + if (y >= 0 && y < HEIGHT && x >= 0 && x < WIDTH) { + + float a0 = (float)q / p; + float a1 = a0 - dq2; + float a2 = a0 + dq2; + + int pos = y * WIDTH + x; + + if (obs.isBlocked( a0 ) && obs.isBlocked( a1 ) && obs.isBlocked( a2 )) { + + } else { + fieldOfView[pos] = true; + } + + if (losBlocking[pos]) { + obs.add( a1, a2 ); + } + + } + } + + obs.nextRow(); + } + } + + private static final class Obstacles { + + private static int SIZE = (MAX_DISTANCE+1) * (MAX_DISTANCE+1) / 2; + private static float[] a1 = new float[SIZE]; + private static float[] a2 = new float[SIZE]; + + private int length; + private int limit; + + public void reset() { + length = 0; + limit = 0; + } + + public void add( float o1, float o2 ) { + + if (length > limit && o1 <= a2[length-1]) { + + a2[length-1] = o2; + + } else { + + a1[length] = o1; + a2[length++] = o2; + + } + + } + + public boolean isBlocked( float a ) { + for (int i=0; i < limit; i++) { + if (a >= a1[i] && a <= a2[i]) { + return true; + } + } + return false; + } + + public void nextRow() { + limit = length; + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Blindweed.java b/src/com/watabou/pixeldungeon/plants/Blindweed.java new file mode 100644 index 0000000000..0e3f806053 --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Blindweed.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Blindness; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.actors.mobs.Mob.State; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.potions.PotionOfInvisibility; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.utils.Random; + +public class Blindweed extends Plant { + + private static final String TXT_DESC = + "Upon touching a Blindweed it perishes in a blinding flash of light."; + + { + image = 3; + plantName = "Blindweed"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + if (ch != null) { + Buff.prolong( ch, Blindness.class, Random.Int( 5, 10 ) ); + if (ch instanceof Mob) { + ((Mob)ch).state = State.WANDERING; + ((Mob)ch).beckon( Dungeon.level.randomDestination() ); + } + } + + if (Dungeon.visible[pos]) { + CellEmitter.get( pos ).burst( Speck.factory( Speck.LIGHT ), 4 ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Blindweed"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_BLINDWEED; + + plantClass = Blindweed.class; + alchemyClass = PotionOfInvisibility.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Earthroot.java b/src/com/watabou/pixeldungeon/plants/Earthroot.java new file mode 100644 index 0000000000..400614ebc5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Earthroot.java @@ -0,0 +1,143 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.noosa.Camera; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.EarthParticle; +import com.watabou.pixeldungeon.items.potions.PotionOfParalyticGas; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.utils.Bundle; + +public class Earthroot extends Plant { + + private static final String TXT_DESC = + "When a creature touches an Earthroot, its roots " + + "create a kind of natural armor around it."; + + { + image = 5; + plantName = "Earthroot"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + if (ch != null) { + Buff.affect( ch, Armor.class ).level = ch.HT; + } + + if (Dungeon.visible[pos]) { + CellEmitter.bottom( pos ).start( EarthParticle.FACTORY, 0.05f, 8 ); + Camera.main.shake( 1, 0.4f ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Earthroot"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_EARTHROOT; + + plantClass = Earthroot.class; + alchemyClass = PotionOfParalyticGas.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } + + public static class Armor extends Buff { + + private static final float STEP = 1f; + + private int pos; + private int level; + + @Override + public boolean attachTo( Char target ) { + pos = target.pos; + return super.attachTo( target ); + } + + @Override + public boolean act() { + if (target.pos != pos) { + detach(); + } + spend( STEP ); + return true; + } + + public int absorb( int damage ) { + if (damage >= level) { + detach(); + return damage - level; + } else { + level -= damage; + return 0; + } + } + + public void level( int value ) { + if (level < value) { + level = value; + } + } + + @Override + public int icon() { + return BuffIndicator.ARMOR; + } + + @Override + public String toString() { + return "Herbal armor"; + } + + private static final String POS = "pos"; + private static final String LEVEL = "level"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( POS, pos ); + bundle.put( LEVEL, level ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + pos = bundle.getInt( POS ); + level = bundle.getInt( LEVEL ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Fadeleaf.java b/src/com/watabou/pixeldungeon/plants/Fadeleaf.java new file mode 100644 index 0000000000..fda8c78e27 --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Fadeleaf.java @@ -0,0 +1,99 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.potions.PotionOfMindVision; +import com.watabou.pixeldungeon.items.scrolls.ScrollOfTeleportation; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Fadeleaf extends Plant { + + private static final String TXT_DESC = + "Touching a Fadeleaf will teleport any creature " + + "to a random place on the current level."; + + { + image = 6; + plantName = "Fadeleaf"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + if (ch instanceof Hero) { + + ScrollOfTeleportation.teleportHero( (Hero)ch ); + ((Hero)ch).curAction = null; + + } else if (ch instanceof Mob) { + + // Why do I try to choose a new position 10 times? + // I don't remember... + int count = 10; + int newPos; + do { + newPos = Dungeon.level.randomRespawnCell(); + if (count-- <= 0) { + break; + } + } while (newPos == -1); + + if (newPos != -1) { + + ch.pos = newPos; + ch.sprite.place( ch.pos ); + ch.sprite.visible = Dungeon.visible[pos]; + + } + + } + + if (Dungeon.visible[pos]) { + CellEmitter.get( pos ).start( Speck.factory( Speck.LIGHT ), 0.2f, 3 ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Fadeleaf"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_FADELEAF; + + plantClass = Fadeleaf.class; + alchemyClass = PotionOfMindVision.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Firebloom.java b/src/com/watabou/pixeldungeon/plants/Firebloom.java new file mode 100644 index 0000000000..8b5e9a19a6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Firebloom.java @@ -0,0 +1,71 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.items.potions.PotionOfLiquidFlame; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Firebloom extends Plant { + + private static final String TXT_DESC = "When something touches a Firebloom, it bursts into flames."; + + { + image = 0; + plantName = "Firebloom"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + GameScene.add( Blob.seed( pos, 2, Fire.class ) ); + + if (Dungeon.visible[pos]) { + CellEmitter.get( pos ).burst( FlameParticle.FACTORY, 5 ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Firebloom"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_FIREBLOOM; + + plantClass = Firebloom.class; + alchemyClass = PotionOfLiquidFlame.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Icecap.java b/src/com/watabou/pixeldungeon/plants/Icecap.java new file mode 100644 index 0000000000..b14084cdab --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Icecap.java @@ -0,0 +1,77 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.blobs.Fire; +import com.watabou.pixeldungeon.actors.blobs.Freezing; +import com.watabou.pixeldungeon.items.potions.PotionOfFrost; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.BArray; +import com.watabou.utils.PathFinder; + +public class Icecap extends Plant { + + private static final String TXT_DESC = + "Upon touching an Icecap excretes a pollen, which freezes everything in its vicinity."; + + { + image = 1; + plantName = "Icecap"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + PathFinder.buildDistanceMap( pos, BArray.not( Level.losBlocking, null ), 1 ); + + Fire fire = (Fire)Dungeon.level.blobs.get( Fire.class ); + + for (int i=0; i < Level.LENGTH; i++) { + if (PathFinder.distance[i] < Integer.MAX_VALUE) { + + Freezing.affect( i, fire ); + } + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Icecap"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_ICECAP; + + plantClass = Icecap.class; + alchemyClass = PotionOfFrost.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Plant.java b/src/com/watabou/pixeldungeon/plants/Plant.java new file mode 100644 index 0000000000..c3632c8edb --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Plant.java @@ -0,0 +1,177 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import java.util.ArrayList; + +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Barkskin; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.LeafParticle; +import com.watabou.pixeldungeon.items.Dewdrop; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.sprites.PlantSprite; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; +import com.watabou.utils.Random; + +public class Plant implements Bundlable { + + public String plantName; + + public int image; + public int pos; + + public PlantSprite sprite; + + public void activate( Char ch ) { + + if (ch instanceof Hero && ((Hero)ch).subClass == HeroSubClass.WARDEN) { + Buff.affect( ch, Barkskin.class ).level( ch.HT / 3 ); + } + + wither(); + } + + public void wither() { + Dungeon.level.uproot( pos ); + + sprite.kill(); + if (Dungeon.visible[pos]) { + CellEmitter.get( pos ).burst( LeafParticle.GENERAL, 6 ); + } + + if (Dungeon.hero.subClass == HeroSubClass.WARDEN) { + if (Random.Int( 5 ) == 0) { + Dungeon.level.drop( Generator.random( Generator.Category.SEED ), pos ).sprite.drop(); + } + if (Random.Int( 5 ) == 0) { + Dungeon.level.drop( new Dewdrop(), pos ).sprite.drop(); + } + } + } + + private static final String POS = "pos"; + + @Override + public void restoreFromBundle( Bundle bundle ) { + pos = bundle.getInt( POS ); + } + + @Override + public void storeInBundle( Bundle bundle ) { + bundle.put( POS, pos ); + } + + public String desc() { + return null; + } + + public static class Seed extends Item { + + public static final String AC_PLANT = "PLANT"; + + private static final String TXT_INFO = "Throw this seed to the place where you want to grow %s.\n\n%s"; + + private static final float TIME_TO_PLANT = 1f; + + { + stackable = true; + defaultAction = AC_THROW; + } + + protected Class plantClass; + protected String plantName; + + public Class alchemyClass; + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + actions.add( AC_PLANT ); + return actions; + } + + @Override + protected void onThrow( int cell ) { + if (Dungeon.level.map[cell] == Terrain.ALCHEMY || Level.pit[cell]) { + super.onThrow( cell ); + } else { + Dungeon.level.plant( this, cell ); + } + } + + @Override + public void execute( Hero hero, String action ) { + if (action.equals( AC_PLANT )) { + + hero.spend( TIME_TO_PLANT ); + hero.busy(); + ((Seed)detach( hero.belongings.backpack )).onThrow( hero.pos ); + + hero.sprite.operate( hero.pos ); + + } else { + + super.execute (hero, action ); + + } + } + + public Plant couch( int pos ) { + try { + Sample.INSTANCE.play( Assets.SND_PLANT ); + Plant plant = plantClass.newInstance(); + plant.pos = pos; + return plant; + } catch (Exception e) { + return null; + } + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public int price() { + return 10 * quantity; + } + + @Override + public String info() { + return String.format( TXT_INFO, Utils.indefinite( plantName ), desc() ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Sorrowmoss.java b/src/com/watabou/pixeldungeon/plants/Sorrowmoss.java new file mode 100644 index 0000000000..54bf56f13a --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Sorrowmoss.java @@ -0,0 +1,73 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.buffs.Poison; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.particles.PoisonParticle; +import com.watabou.pixeldungeon.items.potions.PotionOfToxicGas; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; + +public class Sorrowmoss extends Plant { + + private static final String TXT_DESC = + "A Sorrowmoss is a flower (not a moss) with razor-sharp petals, coated with a deadly venom."; + + { + image = 2; + plantName = "Sorrowmoss"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + if (ch != null) { + Buff.affect( ch, Poison.class ).set( 5 + Math.min( Dungeon.depth, 15 ) ); + } + + if (Dungeon.visible[pos]) { + CellEmitter.center( pos ).burst( PoisonParticle.SPLASH, 3 ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Sorrowmoss"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_SORROWMOSS; + + plantClass = Sorrowmoss.class; + alchemyClass = PotionOfToxicGas.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } +} diff --git a/src/com/watabou/pixeldungeon/plants/Sungrass.java b/src/com/watabou/pixeldungeon/plants/Sungrass.java new file mode 100644 index 0000000000..c1a1e82aba --- /dev/null +++ b/src/com/watabou/pixeldungeon/plants/Sungrass.java @@ -0,0 +1,123 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.plants; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ShaftParticle; +import com.watabou.pixeldungeon.items.potions.PotionOfHealing; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.utils.Bundle; + +public class Sungrass extends Plant { + + private static final String TXT_DESC = "Sungrass is renowned for its sap's healing properties."; + + { + image = 4; + plantName = "Sungrass"; + } + + @Override + public void activate( Char ch ) { + super.activate( ch ); + + if (ch != null) { + Buff.affect( ch, Health.class ); + } + + if (Dungeon.visible[pos]) { + CellEmitter.get( pos ).start( ShaftParticle.FACTORY, 0.2f, 3 ); + } + } + + @Override + public String desc() { + return TXT_DESC; + } + + public static class Seed extends Plant.Seed { + { + plantName = "Sungrass"; + + name = "seed of " + plantName; + image = ItemSpriteSheet.SEED_SUNGRASS; + + plantClass = Sungrass.class; + alchemyClass = PotionOfHealing.class; + } + + @Override + public String desc() { + return TXT_DESC; + } + } + + public static class Health extends Buff { + + private static final float STEP = 5f; + + private int pos; + + @Override + public boolean attachTo( Char target ) { + pos = target.pos; + return super.attachTo( target ); + } + + @Override + public boolean act() { + if (target.pos != pos || target.HP >= target.HT) { + detach(); + } else { + target.HP = Math.min( target.HT, target.HP + target.HT / 10 ); + target.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 ); + } + spend( STEP ); + return true; + } + + @Override + public int icon() { + return BuffIndicator.HEALING; + } + + @Override + public String toString() { + return "Herbal healing"; + } + + private static final String POS = "pos"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( POS, pos ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + pos = bundle.getInt( POS ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/AboutScene.java b/src/com/watabou/pixeldungeon/scenes/AboutScene.java new file mode 100644 index 0000000000..a459f72a95 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/AboutScene.java @@ -0,0 +1,93 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import android.content.Intent; +import android.net.Uri; + +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.TouchArea; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.ui.Archs; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.Window; + +public class AboutScene extends PixelScene { + + private static final String TXT = + "Code & graphics: Watabou\n" + + "Music: Cube_Code\n\n" + + "This game is inspired by Brian Walker's Brogue. " + + "Try it on Windows, Mac OS or Linux - it's awesome! ;)\n\n" + + "Please visit official website for additional info:"; + + private static final String LNK = "pixeldungeon.watabou.ru"; + + @Override + public void create() { + super.create(); + + BitmapTextMultiline text = createMultiline( TXT, 8 ); + text.maxWidth = Math.min( Camera.main.width, 120 ); + text.measure(); + add( text ); + + text.x = align( (Camera.main.width - text.width()) / 2 ); + text.y = align( (Camera.main.height - text.height()) / 2 ); + + BitmapTextMultiline link = createMultiline( LNK, 8 ); + link.maxWidth = Math.min( Camera.main.width, 120 ); + link.measure(); + link.hardlight( Window.TITLE_COLOR ); + add( link ); + + link.x = text.x; + link.y = text.y + text.height(); + + TouchArea hotArea = new TouchArea( link ) { + @Override + protected void onClick( Touch touch ) { + Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( "http://" + LNK ) ); + Game.instance.startActivity( intent ); + } + }; + add( hotArea ); + + Image wata = Icons.WATA.get(); + wata.x = align( text.x + (text.width() - wata.width) / 2 ); + wata.y = text.y - wata.height - 8; + add( wata ); + + new Flare( 7, 64 ).color( 0x112233, true ).show( wata, 0 ).angularSpeed = +20; + + Archs archs = new Archs(); + archs.setSize( Camera.main.width, Camera.main.height ); + addToBack( archs ); + + fadeIn(); + } + + @Override + protected void onBackPressed() { + Game.switchScene( TitleScene.class ); + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/AmuletScene.java b/src/com/watabou/pixeldungeon/scenes/AmuletScene.java new file mode 100644 index 0000000000..2de787018e --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/AmuletScene.java @@ -0,0 +1,133 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.utils.Random; + +public class AmuletScene extends PixelScene { + + private static final String TXT_EXIT = "Let's call it a day"; + private static final String TXT_STAY = "I'm not done yet"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + private static final float SMALL_GAP = 2; + private static final float LARGE_GAP = 8; + + private static final String TXT = + "You finally hold it in your hands, the Amulet of Yendor. Using its power " + + "you can take over the world or bring peace and prosperity to people or whatever. " + + "Anyway, your life will change forever and this game will end here. " + + "Or you can stay a mere mortal a little longer."; + + public static boolean noText = false; + + private Image amulet; + + @Override + public void create() { + super.create(); + + BitmapTextMultiline text = null; + if (!noText) { + text = createMultiline( TXT, 8 ); + text.maxWidth = WIDTH; + text.measure(); + add( text ); + } + + amulet = new Image( Assets.AMULET ); + add( amulet ); + + RedButton btnExit = new RedButton( TXT_EXIT ) { + @Override + protected void onClick() { + Dungeon.deleteGame( Dungeon.hero.heroClass, true ); + Game.switchScene( noText ? TitleScene.class : RankingsScene.class ); + } + }; + btnExit.setSize( WIDTH, BTN_HEIGHT ); + add( btnExit ); + + RedButton btnStay = new RedButton( TXT_STAY ) { + @Override + protected void onClick() { + onBackPressed(); + } + }; + btnStay.setSize( WIDTH, BTN_HEIGHT ); + add( btnStay ); + + float height; + if (noText) { + height = amulet.height + LARGE_GAP + btnExit.height() + SMALL_GAP + btnStay.height(); + + amulet.x = align( (Camera.main.width - amulet.width) / 2 ); + amulet.y = align( (Camera.main.height - height) / 2 ); + + btnExit.setPos( (Camera.main.width - btnExit.width()) / 2, amulet.y + amulet.height + LARGE_GAP ); + btnStay.setPos( btnExit.left(), btnExit.bottom() + SMALL_GAP ); + + } else { + height = amulet.height + LARGE_GAP + text.height() + LARGE_GAP + btnExit.height() + SMALL_GAP + btnStay.height(); + + amulet.x = align( (Camera.main.width - amulet.width) / 2 ); + amulet.y = align( (Camera.main.height - height) / 2 ); + + text.x = align( (Camera.main.width - text.width()) / 2 ); + text.y = amulet.y + amulet.height + LARGE_GAP; + + btnExit.setPos( (Camera.main.width - btnExit.width()) / 2, text.y + text.height() + LARGE_GAP ); + btnStay.setPos( btnExit.left(), btnExit.bottom() + SMALL_GAP ); + } + + new Flare( 8, 48 ).color( 0xFFDDBB, true ).show( amulet, 0 ).angularSpeed = +30; + + fadeIn(); + } + + @Override + protected void onBackPressed() { + InterlevelScene.mode = InterlevelScene.Mode.CONTINUE; + Game.switchScene( InterlevelScene.class ); + } + + private float timer = 0; + + @Override + public void update() { + super.update(); + + if ((timer -= Game.elapsed) < 0) { + timer = Random.Float( 0.5f, 5f ); + + Speck star = (Speck)recycle( Speck.class ); + star.reset( 0, amulet.x + 10.5f, amulet.y + 5.5f, Speck.DISCOVER ); + add( star ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/BadgesScene.java b/src/com/watabou/pixeldungeon/scenes/BadgesScene.java new file mode 100644 index 0000000000..55579c1a65 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/BadgesScene.java @@ -0,0 +1,88 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.audio.Music; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.ui.Archs; +import com.watabou.pixeldungeon.ui.BadgesList; +import com.watabou.pixeldungeon.ui.ScrollPane; +import com.watabou.pixeldungeon.ui.Window; + +public class BadgesScene extends PixelScene { + + private static final String TXT_TITLE = "Your Badges"; + + @Override + public void create() { + + super.create(); + + Music.INSTANCE.play( Assets.THEME, true ); + Music.INSTANCE.volume( 1f ); + + uiCamera.visible = false; + + int w = Camera.main.width; + int h = Camera.main.height; + + Archs archs = new Archs(); + archs.setSize( w, h ); + add( archs ); + + int pw = Math.min( 160, w - 10 ); + int ph = h - 24; + + NinePatch panel = Chrome.get( Chrome.Type.WINDOW ); + panel.size( pw, ph ); + panel.x = (w - pw) / 2; + panel.y = (h - ph) / 2; + add( panel ); + + BitmapText title = PixelScene.createText( TXT_TITLE, 9 ); + title.hardlight( Window.TITLE_COLOR ); + title.measure(); + title.x = align( (w - title.width()) / 2 ); + title.y = align( (panel.y - title.baseLine()) / 2 ); + add( title ); + + Badges.loadGlobal(); + + ScrollPane list = new BadgesList( true ); + add( list ); + + list.setRect( + panel.x + panel.marginLeft(), + panel.y + panel.marginTop(), + panel.innerWidth(), + panel.innerHeight() ); + + fadeIn(); + } + + @Override + protected void onBackPressed() { + Game.switchScene( TitleScene.class ); + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/CellSelector.java b/src/com/watabou/pixeldungeon/scenes/CellSelector.java new file mode 100644 index 0000000000..dbfd1d6d27 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/CellSelector.java @@ -0,0 +1,158 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.TouchArea; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.utils.GameMath; +import com.watabou.utils.PointF; + +public class CellSelector extends TouchArea { + + public Listener listener = null; + + public boolean enabled; + + private float dragThreshold; + + public CellSelector( DungeonTilemap map ) { + super( map ); + camera = map.camera(); + + dragThreshold = PixelScene.defaultZoom * DungeonTilemap.SIZE / 2; + } + + @Override + protected void onClick( Touch touch ) { + if (dragging) { + + dragging = false; + + } else { + + select( ((DungeonTilemap)target).screenToTile( + (int)touch.current.x, + (int)touch.current.y ) ); + } + } + + public void select( int cell ) { + if (enabled && listener != null && cell != -1) { + + listener.onSelect( cell ); + GameScene.ready(); + + } else { + + GameScene.cancel(); + + } + } + + private boolean pinching = false; + private Touch another; + private float startZoom; + private float startSpan; + + @Override + protected void onTouchDown( Touch t ) { + + if (t != touch && another == null) { + + if (!touch.down) { + touch = t; + onTouchDown( t ); + return; + } + + pinching = true; + + another = t; + startSpan = PointF.distance( touch.current, another.current ); + startZoom = camera.zoom; + + dragging = false; + } + } + + @Override + protected void onTouchUp( Touch t ) { + if (pinching && (t == touch || t == another)) { + + pinching = false; + + int zoom = Math.round( camera.zoom ); + camera.zoom( zoom ); + PixelDungeon.zoom( (int)(zoom - PixelScene.defaultZoom) ); + + dragging = true; + if (t == touch) { + touch = another; + } + another = null; + lastPos.set( touch.current ); + } + } + + private boolean dragging = false; + private PointF lastPos = new PointF(); + + @Override + protected void onDrag( Touch t ) { + + camera.target = null; + + if (pinching) { + + float curSpan = PointF.distance( touch.current, another.current ); + camera.zoom( GameMath.gate( + PixelScene.minZoom, + startZoom * curSpan / startSpan, + PixelScene.maxZoom ) ); + + } else { + + if (!dragging && PointF.distance( t.current, t.start ) > dragThreshold) { + + dragging = true; + lastPos.set( t.current ); + + } else if (dragging) { + camera.scroll.offset( PointF.diff( lastPos, t.current ).invScale( camera.zoom ) ); + lastPos.set( t.current ); + } + } + + } + + public void cancel() { + + if (listener != null) { + listener.onSelect( null ); + } + + GameScene.ready(); + } + + public interface Listener { + void onSelect( Integer cell ); + String prompt(); + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/GameScene.java b/src/com/watabou/pixeldungeon/scenes/GameScene.java new file mode 100644 index 0000000000..d6ff401530 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/GameScene.java @@ -0,0 +1,618 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import java.io.IOException; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.SkinnedBlock; +import com.watabou.noosa.Visual; +import com.watabou.noosa.audio.Music; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.FogOfWar; +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.effects.BannerSprites; +import com.watabou.pixeldungeon.effects.BlobEmitter; +import com.watabou.pixeldungeon.effects.EmoIcon; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.effects.FloatingText; +import com.watabou.pixeldungeon.effects.Ripple; +import com.watabou.pixeldungeon.effects.SpellSprite; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.wands.WandOfBlink; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.RegularLevel; +import com.watabou.pixeldungeon.levels.features.Chasm; +import com.watabou.pixeldungeon.plants.Plant; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.sprites.DiscardedItemSprite; +import com.watabou.pixeldungeon.sprites.HeroSprite; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.PlantSprite; +import com.watabou.pixeldungeon.ui.AttackIndicator; +import com.watabou.pixeldungeon.ui.Banner; +import com.watabou.pixeldungeon.ui.BusyIndicator; +import com.watabou.pixeldungeon.ui.GameLog; +import com.watabou.pixeldungeon.ui.HealthIndicator; +import com.watabou.pixeldungeon.ui.QuickSlot; +import com.watabou.pixeldungeon.ui.StatusPane; +import com.watabou.pixeldungeon.ui.Toast; +import com.watabou.pixeldungeon.ui.Toolbar; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.windows.WndBag.Mode; +import com.watabou.pixeldungeon.windows.WndGame; +import com.watabou.pixeldungeon.windows.WndBag; +import com.watabou.pixeldungeon.windows.WndStory; +import com.watabou.utils.Random; + +public class GameScene extends PixelScene { + + private static final String TXT_WELCOME = "Welcome to the level %d of Pixel Dungeon!"; + private static final String TXT_WELCOME_BACK = "Welcome back to the level %d of Pixel Dungeon!"; + private static final String TXT_NIGHT_MODE = "Be cautious, since the dungeon is even more dangerous at night!"; + + private static final String TXT_CHASM = "Your steps echo across the dungeon."; + private static final String TXT_WATER = "You hear the water splashing around you."; + private static final String TXT_GRASS = "The smell of vegetation is thick in the air."; + private static final String TXT_SECRETS = "The atmosphere hints that this floor hides many secrets."; + + static GameScene scene; + + private SkinnedBlock water; + private DungeonTilemap tiles; + private FogOfWar fog; + private HeroSprite hero; + + private GameLog log; + + private BusyIndicator busy; + + private static CellSelector cellSelector; + + private Group terrain; + private Group ripples; + private Group plants; + private Group heaps; + private Group mobs; + private Group emitters; + private Group effects; + private Group gases; + private Group spells; + private Group statuses; + private Group emoicons; + + private Toolbar toolbar; + private Toast prompt; + + @Override + public void create() { + + Music.INSTANCE.play( Assets.TUNE, true ); + Music.INSTANCE.volume( 1f ); + + PixelDungeon.lastClass( Dungeon.hero.heroClass.ordinal() ); + + super.create(); + Camera.main.zoom( defaultZoom + PixelDungeon.zoom() ); + + scene = this; + + terrain = new Group(); + add( terrain ); + + water = new SkinnedBlock( + Level.WIDTH * DungeonTilemap.SIZE, + Level.HEIGHT * DungeonTilemap.SIZE, + Dungeon.level.waterTex() ); + terrain.add( water ); + + ripples = new Group(); + terrain.add( ripples ); + + tiles = new DungeonTilemap(); + terrain.add( tiles ); + + Dungeon.level.addVisuals( this ); + + plants = new Group(); + add( plants ); + + int size = Dungeon.level.plants.size(); + for (int i=0; i < size; i++) { + addPlantSprite( Dungeon.level.plants.valueAt( i ) ); + } + + heaps = new Group(); + add( heaps ); + + size = Dungeon.level.heaps.size(); + for (int i=0; i < size; i++) { + addHeapSprite( Dungeon.level.heaps.valueAt( i ) ); + } + + emitters = new Group(); + effects = new Group(); + emoicons = new Group(); + + mobs = new Group(); + add( mobs ); + + for (Mob mob : Dungeon.level.mobs) { + addMobSprite( mob ); + if (Statistics.amuletObtained) { + mob.beckon( Dungeon.hero.pos ); + } + } + + add( emitters ); + add( effects ); + + gases = new Group(); + add( gases ); + + for (Blob blob : Dungeon.level.blobs.values()) { + blob.emitter = null; + addBlobSprite( blob ); + } + + fog = new FogOfWar( Level.WIDTH, Level.HEIGHT ); + fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped ); + add( fog ); + + brightness( PixelDungeon.brightness() ); + + spells = new Group(); + add( spells ); + + statuses = new Group(); + add( statuses ); + + add( emoicons ); + + hero = new HeroSprite(); + hero.place( Dungeon.hero.pos ); + hero.updateArmor(); + mobs.add( hero ); + + + add( new HealthIndicator() ); + + add( cellSelector = new CellSelector( tiles ) ); + + StatusPane sb = new StatusPane(); + sb.camera = uiCamera; + sb.setSize( uiCamera.width, 0 ); + add( sb ); + + toolbar = new Toolbar(); + toolbar.camera = uiCamera; + toolbar.setRect( 0,uiCamera.height - toolbar.height(), uiCamera.width, toolbar.height() ); + add( toolbar ); + + AttackIndicator attack = new AttackIndicator(); + attack.camera = uiCamera; + attack.setPos( + uiCamera.width - attack.width(), + toolbar.top() - attack.height() ); + add( attack ); + + log = new GameLog(); + log.camera = uiCamera; + log.setRect( 0, toolbar.top(), attack.left(), 0 ); + add( log ); + + if (Dungeon.depth < Statistics.deepestFloor) { + GLog.i( TXT_WELCOME_BACK, Dungeon.depth ); + } else { + GLog.i( TXT_WELCOME, Dungeon.depth ); + Sample.INSTANCE.play( Assets.SND_DESCEND ); + } + switch (Dungeon.level.feeling) { + case CHASM: + GLog.w( TXT_CHASM ); + break; + case WATER: + GLog.w( TXT_WATER ); + break; + case GRASS: + GLog.w( TXT_GRASS ); + break; + default: + } + if (Dungeon.level instanceof RegularLevel && + ((RegularLevel)Dungeon.level).secretDoors > Random.IntRange( 3, 4 )) { + GLog.w( TXT_SECRETS ); + } + if (Dungeon.nightMode && !Dungeon.bossLevel()) { + GLog.w( TXT_NIGHT_MODE ); + } + + busy = new BusyIndicator(); + busy.camera = uiCamera; + busy.x = 1; + busy.y = sb.bottom() + 1; + add( busy ); + + switch (InterlevelScene.mode) { + case RESURRECT: + WandOfBlink.appear( Dungeon.hero, Dungeon.level.entrance ); + new Flare( 8, 32 ).color( 0xFFFF66, true ).show( hero, 2f ) ; + break; + case RETURN: + WandOfBlink.appear( Dungeon.hero, Dungeon.hero.pos ); + break; + case FALL: + Chasm.heroLand(); + break; + case DESCEND: + switch (Dungeon.depth) { + case 1: + WndStory.showChapter( WndStory.ID_SEWERS ); + break; + case 6: + WndStory.showChapter( WndStory.ID_PRISON ); + break; + case 11: + WndStory.showChapter( WndStory.ID_CAVES ); + break; + case 16: + WndStory.showChapter( WndStory.ID_METROPOLIS ); + break; + case 22: + WndStory.showChapter( WndStory.ID_HALLS ); + break; + } + if (Dungeon.hero.isAlive() && Dungeon.depth != 22) { + Badges.validateNoKilling(); + } + break; + default: + } + + Camera.main.target = hero; + fadeIn(); + } + + public void destroy() { + + scene = null; + Badges.saveGlobal(); + + super.destroy(); + } + + @Override + public synchronized void pause() { + try { + Dungeon.saveAll(); + Badges.saveGlobal(); + } catch (IOException e) { + // + } + } + + @Override + public synchronized void update() { + if (Dungeon.hero == null) { + return; + } + + super.update(); + + water.offset( 0, -5 * Game.elapsed ); + + Actor.process(); + + if (Dungeon.hero.ready && !Dungeon.hero.paralysed) { + log.newLine(); + } + + cellSelector.enabled = Dungeon.hero.ready; + } + + @Override + protected void onBackPressed() { + if (!cancel()) { + add( new WndGame() ); + } + } + + @Override + protected void onMenuPressed() { + if (Dungeon.hero.ready) { + selectItem( null, WndBag.Mode.ALL, null ); + } + } + + public void brightness( boolean value ) { + water.rm = water.gm = water.bm = + tiles.rm = tiles.gm = tiles.bm = + value ? 1.5f : 1.0f; + if (value) { + fog.am = +2f; + fog.aa = -1f; + } else { + fog.am = +1f; + fog.aa = 0f; + } + } + + private void addHeapSprite( Heap heap ) { + ItemSprite sprite = heap.sprite = (ItemSprite)heaps.recycle( ItemSprite.class ); + sprite.revive(); + sprite.link( heap ); + heaps.add( sprite ); + } + + private void addDiscardedSprite( Heap heap ) { + heap.sprite = (DiscardedItemSprite)heaps.recycle( DiscardedItemSprite.class ); + heap.sprite.revive(); + heap.sprite.link( heap ); + heaps.add( heap.sprite ); + } + + private void addPlantSprite( Plant plant ) { + (plant.sprite = (PlantSprite)plants.recycle( PlantSprite.class )).reset( plant ); + } + + private void addBlobSprite( final Blob gas ) { + if (gas.emitter == null) { + gases.add( new BlobEmitter( gas ) ); + } + } + + private void addMobSprite( Mob mob ) { + CharSprite sprite = mob.sprite(); + sprite.visible = Dungeon.visible[mob.pos]; + mobs.add( sprite ); + sprite.link( mob ); + } + + private void prompt( String text ) { + + if (prompt != null) { + prompt.killAndErase(); + prompt = null; + } + + if (text != null) { + prompt = new Toast( text ) { + @Override + protected void onClose() { + cancel(); + } + }; + prompt.camera = uiCamera; + prompt.setPos( (uiCamera.width - prompt.width()) / 2, uiCamera.height - 60 ); + add( prompt ); + } + } + + private void showBanner( Banner banner ) { + banner.camera = uiCamera; + banner.x = align( uiCamera, (uiCamera.width - banner.width) / 2 ); + banner.y = align( uiCamera, (uiCamera.height - banner.height) / 3 ); + add( banner ); + } + + // ------------------------------------------------------- + + public static void add( Plant plant ) { + if (scene != null) { + scene.addPlantSprite( plant ); + } + } + + public static void add( Blob gas ) { + Actor.add( gas ); + if (scene != null) { + scene.addBlobSprite( gas ); + } + } + + public static void add( Heap heap ) { + if (scene != null) { + scene.addHeapSprite( heap ); + } + } + + public static void discard( Heap heap ) { + if (scene != null) { + scene.addDiscardedSprite( heap ); + } + } + + public static void add( Mob mob ) { + Dungeon.level.mobs.add( mob ); + Actor.add( mob ); + Actor.occupyCell( mob ); + scene.addMobSprite( mob ); + } + + public static void add( Mob mob, float delay ) { + Dungeon.level.mobs.add( mob ); + Actor.addDelayed( mob, delay ); + Actor.occupyCell( mob ); + scene.addMobSprite( mob ); + } + + public static void add( EmoIcon icon ) { + scene.emoicons.add( icon ); + } + + public static void effect( Visual effect ) { + scene.effects.add( effect ); + } + + public static Ripple ripple( int pos ) { + Ripple ripple = (Ripple)scene.ripples.recycle( Ripple.class ); + ripple.reset( pos ); + return ripple; + } + + public static SpellSprite spellSprite() { + return (SpellSprite)scene.spells.recycle( SpellSprite.class ); + } + + public static Emitter emitter() { + if (scene != null) { + Emitter emitter = (Emitter)scene.emitters.recycle( Emitter.class ); + emitter.revive(); + return emitter; + } else { + return null; + } + } + + public static FloatingText status() { + return scene != null ? (FloatingText)scene.statuses.recycle( FloatingText.class ) : null; + } + + public static void pickUp( Item item ) { + scene.toolbar.pickup( item ); + } + + public static void updateMap() { + if (scene != null) { + scene.tiles.updated.set( 0, 0, Level.WIDTH, Level.HEIGHT ); + } + } + + public static void updateMap( int cell ) { + if (scene != null) { + scene.tiles.updated.union( cell % Level.WIDTH, cell / Level.WIDTH ); + } + } + + public static void discoverTile( int pos, int oldValue ) { + if (scene != null) { + scene.tiles.discover( pos, oldValue ); + } + } + + public static void show( Window wnd ) { + cancelCellSelector(); + scene.add( wnd ); + } + + public static void afterObserve() { + if (scene != null) { + scene.fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped ); + + for (Mob mob : Dungeon.level.mobs) { + mob.sprite.visible = Dungeon.visible[mob.pos]; + } + } + } + + public static void flash( int color ) { + scene.fadeIn( 0xFF000000 | color, true ); + } + + public static void gameOver() { + Banner gameOver = new Banner( BannerSprites.get( BannerSprites.Type.GAME_OVER ) ); + gameOver.show( 0x000000, 1f ); + scene.showBanner( gameOver ); + + Sample.INSTANCE.play( Assets.SND_DEATH ); + } + + public static void bossSlain() { + if (Dungeon.hero.isAlive()) { + Banner bossSlain = new Banner( BannerSprites.get( BannerSprites.Type.BOSS_SLAIN ) ); + bossSlain.show( 0xFFFFFF, 0.3f, 5f ); + scene.showBanner( bossSlain ); + + Sample.INSTANCE.play( Assets.SND_BOSS ); + } + } + + public static void handleCell( int cell ) { + cellSelector.select( cell ); + } + + public static void selectCell( CellSelector.Listener listener ) { + cellSelector.listener = listener; + scene.prompt( listener.prompt() ); + } + + private static boolean cancelCellSelector() { + if (cellSelector.listener != null && cellSelector.listener != defaultCellListener) { + cellSelector.cancel(); + return true; + } else { + return false; + } + } + + public static WndBag selectItem( WndBag.Listener listener, WndBag.Mode mode, String title ) { + cancelCellSelector(); + + WndBag wnd = mode == Mode.SEED ? + WndBag.seedPouch( listener, mode, title ) : + WndBag.lastBag( listener, mode, title ); + scene.add( wnd ); + + return wnd; + } + + static boolean cancel() { + if (Dungeon.hero.curAction != null || Dungeon.hero.restoreHealth) { + + Dungeon.hero.curAction = null; + Dungeon.hero.restoreHealth = false; + return true; + + } else { + + return cancelCellSelector(); + + } + } + + public static void ready() { + selectCell( defaultCellListener ); + QuickSlot.cancel(); + } + + private static final CellSelector.Listener defaultCellListener = new CellSelector.Listener() { + @Override + public void onSelect( Integer cell ) { + if (cell != -1) { + Dungeon.hero.handle( cell ); + } + } + @Override + public String prompt() { + return null; + } + }; +} diff --git a/src/com/watabou/pixeldungeon/scenes/InterlevelScene.java b/src/com/watabou/pixeldungeon/scenes/InterlevelScene.java new file mode 100644 index 0000000000..091470ae58 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/InterlevelScene.java @@ -0,0 +1,335 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import java.io.FileNotFoundException; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.audio.Music; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.items.Generator; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.windows.WndError; +import com.watabou.pixeldungeon.windows.WndStory; + +public class InterlevelScene extends PixelScene { + + private static final float TIME_TO_FADE = 0.3f; + + private static final String TXT_DESCENDING = "Descending..."; + private static final String TXT_ASCENDING = "Ascending..."; + private static final String TXT_LOADING = "Loading..."; + private static final String TXT_RESURRECTING= "Resurrecting..."; + private static final String TXT_RETURNING = "Returning..."; + private static final String TXT_FALLING = "Falling..."; + + private static final String ERR_FILE_NOT_FOUND = "File not found. For some reason."; + private static final String ERR_GENERIC = "Something went wrong..." ; + + public static enum Mode { + DESCEND, ASCEND, CONTINUE, RESURRECT, RETURN, FALL + }; + public static Mode mode; + + public static int returnDepth; + public static int returnPos; + + public static boolean noStory = false; + + public static boolean fallIntoPit; + + private enum Phase { + FADE_IN, STATIC, FADE_OUT + }; + private Phase phase; + private float timeLeft; + + private BitmapText message; + + private Thread thread; + private String error = null; + + @Override + public void create() { + super.create(); + + String text = ""; + switch (mode) { + case DESCEND: + text = TXT_DESCENDING; + break; + case ASCEND: + text = TXT_ASCENDING; + break; + case CONTINUE: + text = TXT_LOADING; + break; + case RESURRECT: + text = TXT_RESURRECTING; + break; + case RETURN: + text = TXT_RETURNING; + break; + case FALL: + text = TXT_FALLING; + break; + } + + message = PixelScene.createText( text, 9 ); + message.measure(); + message.x = (Camera.main.width - message.width()) / 2; + message.y = (Camera.main.height - message.height()) / 2; + add( message ); + + phase = Phase.FADE_IN; + timeLeft = TIME_TO_FADE; + + thread = new Thread() { + @Override + public void run() { + + try { + + Generator.reset(); + + Sample.INSTANCE.load( + Assets.SND_OPEN, + Assets.SND_UNLOCK, + Assets.SND_ITEM, + Assets.SND_DEWDROP, + Assets.SND_HIT, + Assets.SND_MISS, + Assets.SND_STEP, + Assets.SND_WATER, + Assets.SND_DESCEND, + Assets.SND_EAT, + Assets.SND_READ, + Assets.SND_LULLABY, + Assets.SND_DRINK, + Assets.SND_SHATTER, + Assets.SND_ZAP, + Assets.SND_LIGHTNING, + Assets.SND_LEVELUP, + Assets.SND_DEATH, + Assets.SND_CHALLENGE, + Assets.SND_CURSED, + Assets.SND_EVOKE, + Assets.SND_TRAP, + Assets.SND_TOMB, + Assets.SND_ALERT, + Assets.SND_MELD, + Assets.SND_BOSS, + Assets.SND_BLAST, + Assets.SND_PLANT, + Assets.SND_RAY, + Assets.SND_BEACON, + Assets.SND_TELEPORT, + Assets.SND_CHARMS, + Assets.SND_MASTERY, + Assets.SND_PUFF, + Assets.SND_ROCKS, + Assets.SND_BURNING, + Assets.SND_FALLING, + Assets.SND_GHOST, + Assets.SND_SECRET, + Assets.SND_BONES ); + + switch (mode) { + case DESCEND: + descend(); + break; + case ASCEND: + ascend(); + break; + case CONTINUE: + restore(); + break; + case RESURRECT: + resurrect(); + break; + case RETURN: + returnTo(); + break; + case FALL: + fall(); + break; + } + + if ((Dungeon.depth % 5) == 0) { + Sample.INSTANCE.load( Assets.SND_BOSS ); + } + + } catch (FileNotFoundException e) { + + error = ERR_FILE_NOT_FOUND; + + } catch (Exception e ) { + + error = ERR_GENERIC; + + } + + if (phase == Phase.STATIC && error == null) { + phase = Phase.FADE_OUT; + timeLeft = TIME_TO_FADE; + } + } + }; + thread.start(); + } + + @Override + public void update() { + super.update(); + + float p = timeLeft / TIME_TO_FADE; + + switch (phase) { + + case FADE_IN: + message.alpha( 1 - p ); + if ((timeLeft -= Game.elapsed) <= 0) { + if (!thread.isAlive() && error == null) { + phase = Phase.FADE_OUT; + timeLeft = TIME_TO_FADE; + } else { + phase = Phase.STATIC; + } + } + break; + + case FADE_OUT: + message.alpha( p ); + + if (mode == Mode.CONTINUE || (mode == Mode.DESCEND && Dungeon.depth == 1)) { + Music.INSTANCE.volume( p ); + } + if ((timeLeft -= Game.elapsed) <= 0) { + Game.switchScene( GameScene.class ); + } + break; + + case STATIC: + if (error != null) { + add( new WndError( error ) { + public void onBackPressed() { + super.onBackPressed(); + Game.switchScene( StartScene.class ); + }; + } ); + } + break; + } + } + + private void descend() throws Exception { + + Actor.fixTime(); + if (Dungeon.hero == null) { + Dungeon.init(); + if (noStory) { + Dungeon.chapters.add( WndStory.ID_SEWERS ); + noStory = false; + } + } else { + Dungeon.saveLevel(); + } + + Level level; + if (Dungeon.depth >= Statistics.deepestFloor) { + level = Dungeon.newLevel(); + } else { + Dungeon.depth++; + level = Dungeon.loadLevel( Dungeon.hero.heroClass ); + } + Dungeon.switchLevel( level, level.entrance ); + } + + private void fall() throws Exception { + + Actor.fixTime(); + Dungeon.saveLevel(); + + Level level; + if (Dungeon.depth >= Statistics.deepestFloor) { + level = Dungeon.newLevel(); + } else { + Dungeon.depth++; + level = Dungeon.loadLevel( Dungeon.hero.heroClass ); + } + Dungeon.switchLevel( level, fallIntoPit ? level.pitCell() : level.randomRespawnCell() ); + } + + private void ascend() throws Exception { + Actor.fixTime(); + + Dungeon.saveLevel(); + Dungeon.depth--; + Level level = Dungeon.loadLevel( Dungeon.hero.heroClass ); + Dungeon.switchLevel( level, level.exit ); + } + + private void returnTo() throws Exception { + + Actor.fixTime(); + + Dungeon.saveLevel(); + Dungeon.depth = returnDepth; + Level level = Dungeon.loadLevel( Dungeon.hero.heroClass ); + Dungeon.switchLevel( level, Level.resizingNeeded ? level.adjustPos( returnPos ) : returnPos ); + } + + private void restore() throws Exception { + + Actor.fixTime(); + + Dungeon.loadGame( StartScene.curClass ); + if (Dungeon.depth == -1) { + Dungeon.depth = Statistics.deepestFloor; + Dungeon.switchLevel( Dungeon.loadLevel( StartScene.curClass ), -1 ); + } else { + Level level = Dungeon.loadLevel( StartScene.curClass ); + Dungeon.switchLevel( level, Level.resizingNeeded ? level.adjustPos( Dungeon.hero.pos ) : Dungeon.hero.pos ); + } + } + + private void resurrect() throws Exception { + + Actor.fixTime(); + + if (Dungeon.bossLevel()) { + Dungeon.hero.resurrect( Dungeon.depth ); + Dungeon.depth--; + Level level = Dungeon.newLevel(/* true */); + Dungeon.switchLevel( level, level.entrance ); + } else { + Dungeon.hero.resurrect( -1 ); + Dungeon.resetLevel(); + } + } + + @Override + protected void onBackPressed() { + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/IntroScene.java b/src/com/watabou/pixeldungeon/scenes/IntroScene.java new file mode 100644 index 0000000000..a28d32ec07 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/IntroScene.java @@ -0,0 +1,47 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.windows.WndStory; + +public class IntroScene extends PixelScene { + + private static final String TEXT = + "Many heroes of all kinds ventured into the Dungeon before you. Some of them have returned with treasures and magical " + + "artifacts, most have never been heard of since. But none have succeeded in retrieving the Amulet of Yendor, " + + "which is told to be hidden in the depths of the Dungeon.\n\n" + + "" + + "You consider yourself ready for the challenge, but most importantly, you feel that fortune smiles on you. " + + "It's time to start your own adventure!"; + + @Override + public void create() { + super.create(); + + add( new WndStory( TEXT ) { + @Override + public void hide() { + super.hide(); + Game.switchScene( InterlevelScene.class ); + } + } ); + + fadeIn(); + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/PixelScene.java b/src/com/watabou/pixeldungeon/scenes/PixelScene.java new file mode 100644 index 0000000000..898c49dbb8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/PixelScene.java @@ -0,0 +1,325 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.input.Touchscreen; +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.BitmapText.Font; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Camera; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.Game; +import com.watabou.noosa.Scene; +import com.watabou.noosa.Visual; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.pixeldungeon.effects.BadgeBanner; +import com.watabou.utils.BitmapCache; + +public class PixelScene extends Scene { + + public static final float MIN_WIDTH = 128; + public static final float MIN_HEIGHT = 224; + + public static float defaultZoom = 0; + public static float minZoom; + public static float maxZoom; + + public static boolean landscapeAvailable; + + public static Camera uiCamera; + + public static BitmapText.Font font1x; + public static BitmapText.Font font15x; + public static BitmapText.Font font2x; + public static BitmapText.Font font25x; + public static BitmapText.Font font3x; + + @Override + public void create() { + + super.create(); + + GameScene.scene = null; + + defaultZoom = (int)Math.ceil( Game.density * 2.5 ); + while (( + Game.width / defaultZoom < MIN_WIDTH || + Game.height / defaultZoom < MIN_HEIGHT + ) && defaultZoom > 1) { + + defaultZoom--; + } + + landscapeAvailable = + Game.height / defaultZoom >= MIN_WIDTH && + Game.width / defaultZoom >= MIN_HEIGHT; + + if (PixelDungeon.scaleUp()) { + while ((Game.width / (defaultZoom + 1) >= MIN_WIDTH && Game.height / (defaultZoom + 1) >= MIN_HEIGHT)) { + defaultZoom++; + } + } + minZoom = 1; + maxZoom = defaultZoom * 2; + + + Camera.reset( new PixelCamera( defaultZoom ) ); + + float uiZoom = defaultZoom; + uiCamera = Camera.createFullscreen( uiZoom ); + Camera.add( uiCamera ); + + if (font1x == null) { + + // 3x5 (6) + font1x = Font.colorMarked( + BitmapCache.get( Assets.FONTS1X ), 0x00000000, BitmapText.Font.LATIN_FULL ); + font1x.baseLine = 6; + font1x.tracking = -1; + + // 5x8 (10) + font15x = Font.colorMarked( + BitmapCache.get( Assets.FONTS15X ), 12, 0x00000000, BitmapText.Font.LATIN_FULL ); + font15x.baseLine = 9; + font15x.tracking = -1; + + // 6x10 (12) + font2x = Font.colorMarked( + BitmapCache.get( Assets.FONTS2X ), 14, 0x00000000, BitmapText.Font.LATIN_FULL ); + font2x.baseLine = 11; + font2x.tracking = -1; + + // 7x12 (15) + font25x = Font.colorMarked( + BitmapCache.get( Assets.FONTS25X ), 17, 0x00000000, BitmapText.Font.LATIN_FULL ); + font25x.baseLine = 13; + font25x.tracking = -1; + + // 9x15 (18) + font3x = Font.colorMarked( + BitmapCache.get( Assets.FONTS3X ), 22, 0x00000000, BitmapText.Font.LATIN_FULL ); + font3x.baseLine = 17; + font3x.tracking = -2; + } + + Sample.INSTANCE.load( + Assets.SND_CLICK, + Assets.SND_BADGE, + Assets.SND_GOLD ); + } + + @Override + public void destroy() { + super.destroy(); + Touchscreen.event.removeAll(); + } + + public static BitmapText.Font font; + public static float scale; + + public static void chooseFont( float size ) { + + float pt = size * defaultZoom; + + if (pt >= 19) { + + scale = pt / 19; + if (1.5 <= scale && scale < 2) { + font = font25x; + scale = (int)(pt / 14); + } else { + font = font3x; + scale = (int)scale; + } + + } else if (pt >= 14) { + + scale = pt / 14; + if (1.8 <= scale && scale < 2) { + font = font2x; + scale = (int)(pt / 12); + } else { + font = font25x; + scale = (int)scale; + } + + } else if (pt >= 12) { + + scale = pt / 12; + if (1.7 <= scale && scale < 2) { + font = font15x; + scale = (int)(pt / 10); + } else { + font = font2x; + scale = (int)scale; + } + + } else if (pt >= 10) { + + scale = pt / 10; + if (1.4 <= scale && scale < 2) { + font = font1x; + scale = (int)(pt / 7); + } else { + font = font15x; + scale = (int)scale; + } + + } else { + + font = font1x; + scale = Math.max( 1, (int)(pt / 7) ); + + } + + scale /= defaultZoom; + } + + public static BitmapText createText( float size ) { + return createText( null, size ); + } + + public static BitmapText createText( String text, float size ) { + + chooseFont( size ); + + BitmapText result = new BitmapText( text, font ); + result.scale.set( scale ); + + return result; + } + + public static BitmapTextMultiline createMultiline( float size ) { + return createMultiline( null, size ); + } + + public static BitmapTextMultiline createMultiline( String text, float size ) { + + chooseFont( size ); + + BitmapTextMultiline result = new BitmapTextMultiline( text, font ); + result.scale.set( scale ); + + return result; + } + + public static float align( Camera camera, float pos ) { + return ((int)(pos * camera.zoom)) / camera.zoom; + } + + public static float align( float pos ) { + return ((int)(pos * defaultZoom)) / defaultZoom; + } + + public static void align( Visual v ) { + Camera c = v.camera(); + v.x = align( c, v.x ); + v.y = align( c, v.y ); + } + + protected void fadeIn() { + fadeIn( 0xFF000000, false ); + } + + protected void fadeIn( int color, boolean light ) { + add( new Fader( color, light ) ); + } + + public static void showBadge( Badges.Badge badge ) { + BadgeBanner banner = BadgeBanner.show( badge.image ); + banner.camera = uiCamera; + banner.x = align( banner.camera, (banner.camera.width - banner.width) / 2 ); + banner.y = align( banner.camera, (banner.camera.height - banner.height) / 3 ); + Game.scene().add( banner ); + } + + protected static class Fader extends ColorBlock { + + private static float FADE_TIME = 1f; + + private boolean light; + + private float time; + + public Fader( int color, boolean light ) { + super( uiCamera.width, uiCamera.height, color ); + + this.light = light; + + camera = uiCamera; + + alpha( 1f ); + time = FADE_TIME; + } + + @Override + public void update() { + + super.update(); + + if ((time -= Game.elapsed) <= 0) { + alpha( 0f ); + parent.remove( this ); + } else { + alpha( time / FADE_TIME ); + } + } + + @Override + public void draw() { + if (light) { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } else { + super.draw(); + } + } + } + + private static class PixelCamera extends Camera { + + public PixelCamera( float zoom ) { + super( + (int)(Game.width - Math.ceil( Game.width / zoom ) * zoom) / 2, + (int)(Game.height - Math.ceil( Game.height / zoom ) * zoom) / 2, + (int)Math.ceil( Game.width / zoom ), + (int)Math.ceil( Game.height / zoom ), zoom ); + } + + @Override + protected void updateMatrix() { + float sx = align( this, scroll.x + shakeX ); + float sy = align( this, scroll.y + shakeY ); + + matrix[0] = +zoom * invW2; + matrix[5] = -zoom * invH2; + + matrix[12] = -1 + x * invW2 - sx * matrix[0]; + matrix[13] = +1 - y * invH2 - sy * matrix[5]; + + } + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/RankingsScene.java b/src/com/watabou/pixeldungeon/scenes/RankingsScene.java new file mode 100644 index 0000000000..36f9d04460 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/RankingsScene.java @@ -0,0 +1,219 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Music; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Rankings; +import com.watabou.pixeldungeon.effects.Flare; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.ui.Archs; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndError; +import com.watabou.pixeldungeon.windows.WndRanking; + +public class RankingsScene extends PixelScene { + + private static final String TXT_TITLE = "Top Rankings"; + private static final String TXT_TOTAL = "Total games played: %d"; + private static final String TXT_NO_GAMES = "No games have been played yet."; + + private static final String TXT_NO_INFO = "No additional information"; + + private static final float ROW_HEIGHT = 30; + private static final float GAP = 4; + + private Archs archs; + + @Override + public void create() { + + super.create(); + + Music.INSTANCE.play( Assets.THEME, true ); + Music.INSTANCE.volume( 1f ); + + uiCamera.visible = false; + + int w = Camera.main.width; + int h = Camera.main.height; + + archs = new Archs(); + archs.setSize( w, h ); + add( archs ); + + Rankings.INSTANCE.load(); + + if (Rankings.INSTANCE.records.size() > 0) { + + float left = (w - Math.min( 160, w )) / 2 + GAP; + float top = align( (h - ROW_HEIGHT * Rankings.INSTANCE.records.size()) / 2 ); + + BitmapText title = PixelScene.createText( TXT_TITLE, 9 ); + title.hardlight( Window.TITLE_COLOR ); + title.measure(); + title.x = align( (w - title.width()) / 2 ); + title.y = align( top - title.height() - GAP ); + add( title ); + + int pos = 0; + + for (Rankings.Record rec : Rankings.INSTANCE.records) { + Record row = new Record( pos, pos == Rankings.INSTANCE.lastRecord, rec ); + row.setRect( left, top + pos * ROW_HEIGHT, w - left * 2, ROW_HEIGHT ); + add( row ); + + pos++; + } + + if (Rankings.INSTANCE.totalNumber >= Rankings.TABLE_SIZE) { + BitmapText total = PixelScene.createText( Utils.format( TXT_TOTAL, Rankings.INSTANCE.totalNumber ), 8 ); + total.hardlight( Window.TITLE_COLOR ); + total.measure(); + total.x = align( (w - total.width()) / 2 ); + total.y = align( top + pos * ROW_HEIGHT + GAP ); + add( total ); + } + + } else { + + BitmapText title = PixelScene.createText( TXT_NO_GAMES, 8 ); + title.hardlight( Window.TITLE_COLOR ); + title.measure(); + title.x = align( (w - title.width()) / 2 ); + title.y = align( (h - title.height()) / 2 ); + add( title ); + + } + + fadeIn(); + } + + @Override + protected void onBackPressed() { + Game.switchScene( TitleScene.class ); + } + + public static class Record extends Button { + + private static final float GAP = 4; + + private static final int TEXT_WIN = 0xFFFF88; + private static final int TEXT_LOSE = 0xCCCCCC; + private static final int FLARE_WIN = 0x888866; + private static final int FLARE_LOSE = 0x666666; + + private Rankings.Record rec; + + private ItemSprite shield; + private Flare flare; + private BitmapText position; + private BitmapTextMultiline desc; + private Image classIcon; + + public Record( int pos, boolean latest, Rankings.Record rec ) { + super(); + + this.rec = rec; + + if (latest) { + flare = new Flare( 6, 24 ); + flare.angularSpeed = 90; + flare.color( rec.win ? FLARE_WIN : FLARE_LOSE ); + addToBack( flare ); + } + + position.text( Integer.toString( pos+1 ) ); + position.measure(); + + desc.text( rec.info ); + desc.measure(); + + if (rec.win) { + shield.view( ItemSpriteSheet.AMULET, null ); + position.hardlight( TEXT_WIN ); + desc.hardlight( TEXT_WIN ); + } else { + position.hardlight( TEXT_LOSE ); + desc.hardlight( TEXT_LOSE ); + } + + classIcon.copy( Icons.get( rec.heroClass ) ); + } + + @Override + protected void createChildren() { + + super.createChildren(); + + shield = new ItemSprite( ItemSpriteSheet.TOMB, null ); + add( shield ); + + position = new BitmapText( PixelScene.font1x ); + add( position ); + + desc = createMultiline( 9 ); + add( desc ); + + classIcon = new Image(); + add( classIcon ); + } + + @Override + protected void layout() { + + super.layout(); + + shield.x = x; + shield.y = y + (height - shield.height) / 2; + + position.x = align( shield.x + (shield.width - position.width()) / 2 ); + position.y = align( shield.y + (shield.height - position.height()) / 2 + 1 ); + + if (flare != null) { + flare.point( shield.center() ); + } + + classIcon.x = align( x + width - classIcon.width ); + classIcon.y = shield.y; + + desc.x = shield.x + shield.width + GAP; + desc.maxWidth = (int)(classIcon.x - desc.x); + desc.measure(); + desc.y = position.y + position.baseLine() - desc.baseLine(); + } + + @Override + protected void onClick() { + if (rec.gameFile.length() > 0) { + parent.add( new WndRanking( rec.gameFile ) ); + } else { + parent.add( new WndError( TXT_NO_INFO ) ); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/StartScene.java b/src/com/watabou/pixeldungeon/scenes/StartScene.java new file mode 100644 index 0000000000..8bd55f37c5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/StartScene.java @@ -0,0 +1,510 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import java.util.HashMap; + +import com.watabou.input.Touchscreen; +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.TouchArea; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.GamesInProgress; +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.ui.Archs; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.SimpleButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.pixeldungeon.windows.WndList; +import com.watabou.pixeldungeon.windows.WndOptions; +import com.watabou.pixeldungeon.windows.WndTitledMessage; + +public class StartScene extends PixelScene { + + private static final float BUTTON_HEIGHT = 24; + private static final float GAP = 2; + + private static final String TXT_TITLE = "Select Your Hero"; + + private static final String TXT_LOAD = "Load Game"; + private static final String TXT_NEW = "New Game"; + + private static final String TXT_ERASE = "Erase current game"; + private static final String TXT_DPTH_LVL = "Depth: %d, level: %d"; + + private static final String TXT_REALLY = "Do you really want to start new game?"; + private static final String TXT_WARNING = "Your current game progress will be erased."; + private static final String TXT_YES = "Yes, start new game"; + private static final String TXT_NO = "No, return to main menu"; + + private static final String TXT_UNLOCK = "To unlock this character class, slay the 3rd boss with any other class"; + + private float width; + private float height; + private float top; + private float left; + + private static HashMap gems = new HashMap(); + + private Avatar avatar; + private NinePatch frame; + private BitmapText className; + + private SimpleButton btnMastery; + + private GameButton btnLoad; + private GameButton btnNewGame; + + private boolean huntressUnlocked; + private Group unlock; + + public static HeroClass curClass; + + @Override + public void create() { + + super.create(); + + Badges.loadGlobal(); + + uiCamera.visible = false; + + int w = Camera.main.width; + int h = Camera.main.height; + + width = 128; + height = 220; + left = (w - width) / 2; + top = (h - height) / 2; + + Archs archs = new Archs(); + archs.setSize( w, h ); + add( archs ); + + BitmapText title = PixelScene.createText( TXT_TITLE, 9 ); + title.hardlight( Window.TITLE_COLOR ); + title.measure(); + title.x = align( (w - title.width()) / 2 ); + title.y = align( top ); + add( title ); + + float pos = title.y + title.height() + GAP; + + GemButton btns[] = { + new GemButton( HeroClass.WARRIOR ), + new GemButton( HeroClass.MAGE ), + new GemButton( HeroClass.ROGUE ), + new GemButton( HeroClass.HUNTRESS ) }; + + float space = width; + for (GemButton btn : btns) { + space -= btn.width(); + } + + float p = 0; + for (GemButton btn : btns) { + add( btn ); + btn.setPos( align( left + p ), align( pos ) ); + p += btn.width() + space / 3; + } + + + frame = Chrome.get( Chrome.Type.TOAST_TR ); + add( frame ); + + btnNewGame = new GameButton( TXT_NEW ) { + @Override + protected void onClick() { + if (GamesInProgress.check( curClass ) != null) { + StartScene.this.add( new WndOptions( TXT_REALLY, TXT_WARNING, TXT_YES, TXT_NO ) { + @Override + protected void onSelect( int index ) { + if (index == 0) { + startNewGame(); + } + } + } ); + + } else { + startNewGame(); + } + } + }; + add( btnNewGame ); + + btnLoad = new GameButton( TXT_LOAD ) { + @Override + protected void onClick() { + InterlevelScene.mode = InterlevelScene.Mode.CONTINUE; + Game.switchScene( InterlevelScene.class ); + } + }; + add( btnLoad ); + + frame.size( width, BUTTON_HEIGHT + frame.marginVer() ); + frame.x = align( left ); + frame.y = align( h - top - frame.height() ); + + avatar = new Avatar(); + + NinePatch avFrame = Chrome.get( Chrome.Type.TOAST_TR ); + avFrame.size( avatar.width() * 1.6f, avatar.height() * 1.6f ); + avFrame.x = align( (w - avFrame.width()) / 2 ); + avFrame.y = align( (frame.y + btns[0].bottom() - avFrame.height()) / 2 ); + add( avFrame ); + + className = PixelScene.createText( "Placeholder", 9 ); + className.measure(); + className.y = align( avFrame.y + avFrame.innerBottom() - className.height() ); + add( className ); + + avatar.point( avFrame.center() ); + avatar.camera = Camera.main; + align( avatar ); + add( avatar ); + + Image iconInfo = Icons.INFO.get(); + iconInfo.x = avFrame.x + avFrame.innerRight() - iconInfo.width(); + iconInfo.y = avFrame.y + avFrame.marginTop(); + add( iconInfo ); + + add( new TouchArea( avFrame ) { + @Override + protected void onClick( Touchscreen.Touch touch ) { + add( new WndList( curClass.perks() ) ); + } + } ); + + btnMastery = new SimpleButton( Icons.MASTERY.get() ) { + @Override + protected void onClick() { + String text = null; + switch (curClass) { + case WARRIOR: + text = HeroSubClass.GLADIATOR.desc() + "\n\n" + HeroSubClass.BERSERKER.desc(); + break; + case MAGE: + text = HeroSubClass.BATTLEMAGE.desc() + "\n\n" + HeroSubClass.WARLOCK.desc(); + break; + case ROGUE: + text = HeroSubClass.FREERUNNER.desc() + "\n\n" + HeroSubClass.ASSASSIN.desc(); + break; + case HUNTRESS: + text = HeroSubClass.SNIPER.desc() + "\n\n" + HeroSubClass.WARDEN.desc(); + break; + } + StartScene.this.add( new WndTitledMessage( Icons.MASTERY.get(), "Mastery", text ) ); + } + }; + btnMastery.setPos( + avFrame.x + avFrame.innerRight() - btnMastery.width(), + avFrame.y + avFrame.innerBottom() - btnMastery.height() ); + add( btnMastery ); + + unlock = new Group(); + add( unlock ); + + if (!(huntressUnlocked = Badges.isUnlocked( Badges.Badge.BOSS_SLAIN_3 ))) { + + BitmapTextMultiline text = PixelScene.createMultiline( TXT_UNLOCK, 5 ); + text.maxWidth = (int)frame.innerWidth(); + text.measure(); + + pos = frame.center().y - text.height() / 2; + for (BitmapText line : text.new LineSplitter().split()) { + line.measure(); + line.hardlight( 0xFFFF00 ); + line.x = PixelScene.align( frame.center().x - line.width() / 2 ); + line.y = PixelScene.align( pos ); + unlock.add( line ); + + pos += line.height(); + } + } + + curClass = null; + updateClass( HeroClass.values()[PixelDungeon.lastClass()] ); + + fadeIn(); + } + + private void updateClass( HeroClass cl ) { + + if (curClass == cl) { + return; + } + + if (curClass != null) { + gems.get( curClass ).highlight( false ); + } + + gems.get( curClass = cl ).highlight( true ); + + className.text( Utils.capitalize( cl.title() ) ); + className.measure(); + className.x = align( frame.center().x - className.width() / 2 ); + + if (cl != HeroClass.HUNTRESS || huntressUnlocked) { + + unlock.visible = false; + + float buttonPos = frame.y + frame.innerBottom() - BUTTON_HEIGHT; + + GamesInProgress.Info info = GamesInProgress.check( curClass ); + if (info != null) { + + btnLoad.visible = true; + btnLoad.secondary( Utils.format( TXT_DPTH_LVL, info.depth, info.level ) ); + btnNewGame.visible = true; + btnNewGame.secondary( TXT_ERASE ); + + float w = (frame.innerWidth() - GAP) / 2; + + btnLoad.setRect( + frame.x + frame.marginLeft(), buttonPos, w, BUTTON_HEIGHT ); + btnNewGame.setRect( + btnLoad.right() + GAP, buttonPos, w, BUTTON_HEIGHT ); + + } else { + btnLoad.visible = false; + + btnNewGame.visible = true; + btnNewGame.secondary( null ); + btnNewGame.setRect( + frame.x + frame.marginLeft(), buttonPos, frame.innerWidth(), BUTTON_HEIGHT ); + } + + Badges.Badge badgeToCheck = null; + switch (curClass) { + case WARRIOR: + badgeToCheck = Badges.Badge.MASTERY_WARRIOR; + break; + case MAGE: + badgeToCheck = Badges.Badge.MASTERY_MAGE; + break; + case ROGUE: + badgeToCheck = Badges.Badge.MASTERY_ROGUE; + break; + case HUNTRESS: + badgeToCheck = Badges.Badge.MASTERY_HUNTRESS; + break; + } + btnMastery.active = + btnMastery.visible = + Badges.isUnlocked( badgeToCheck ); + + } else { + + unlock.visible = true; + btnLoad.visible = false; + btnNewGame.visible = false; + btnMastery.active = btnMastery.visible = false; + + } + + avatar.selectClass( curClass ); + } + + private void startNewGame() { + + Dungeon.hero = null; + InterlevelScene.mode = InterlevelScene.Mode.DESCEND; + + if (PixelDungeon.intro()) { + PixelDungeon.intro( false ); + Game.switchScene( IntroScene.class ); + } else { + Game.switchScene( InterlevelScene.class ); + } + } + + @Override + protected void onBackPressed() { + Game.switchScene( TitleScene.class ); + } + + private static class Avatar extends Image { + + private static final int WIDTH = 24; + private static final int HEIGHT = 32; + private static final int SCALE = 2; + + private TextureFilm frames; + + private float brightness = 0; + + public Avatar() { + super( Assets.AVATARS ); + + frames = new TextureFilm( texture, WIDTH, HEIGHT ); + selectClass( HeroClass.WARRIOR ); + scale.set( SCALE ); + + origin.set( width() / 2, height() / 2 ); + } + + public void selectClass( HeroClass cl ) { + frame( frames.get( cl.ordinal() ) ); + } + + public void flash() { + brightness = 1f; + } + + @Override + public void update() { + super.update(); + + if (brightness > 0) { + ra = ga = ba = brightness; + brightness -= Game.elapsed * 4; + if (brightness < 0) { + resetColor(); + } + } + } + } + + private class GemButton extends Button { + + private NinePatch bg; + private Image icon; + + private HeroClass cl; + + public GemButton( HeroClass cl ) { + super(); + + this.cl = cl; + gems.put( cl, this ); + + icon.copy( Icons.get( cl ) ); + setSize( 32, 32 ); + + highlight( false ); + } + + @Override + protected void createChildren() { + super.createChildren(); + + bg = Chrome.get( Chrome.Type.GEM ); + add( bg ); + + icon = new Image(); + add( icon ); + } + + @Override + protected void layout() { + super.layout(); + + bg.x = x; + bg.y = y; + bg.size( width, height ); + + icon.x = x + (width - icon.width) / 2; + icon.y = y + (height - icon.height) / 2; + } + + @Override + protected void onTouchDown() { + Emitter emitter = (Emitter)recycle( Emitter.class ); + emitter.revive(); + emitter.pos( bg ); + emitter.burst( Speck.factory( Speck.LIGHT ), 3 ); + + updateClass( cl ); + avatar.flash(); + + Sample.INSTANCE.play( Assets.SND_CLICK, 1, 1, 1.2f ); + } + + public void highlight( boolean value ) { + if (value) { + bg.rm = 1.2f; + bg.gm = 1.2f; + bg.bm = 1.1f; + bg.am = 0.8f; + } else { + bg.rm = 1.0f; + bg.gm = 1.0f; + bg.bm = 1.0f; + bg.am = 0.6f; + } + } + } + + private static class GameButton extends RedButton { + + private static final int SECONDARY_COLOR = 0xCACFC2; + + private BitmapText secondary; + + public GameButton( String primary ) { + super( primary ); + + this.secondary.text( null ); + } + + @Override + protected void createChildren() { + super.createChildren(); + + secondary = createText( 6 ); + secondary.hardlight( SECONDARY_COLOR ); + add( secondary ); + } + + @Override + protected void layout() { + super.layout(); + + if (secondary.text().length() > 0) { + text.y = y + (height - text.height() - secondary.baseLine()) / 2; + + secondary.x = align( x + (width - secondary.width()) / 2 ); + secondary.y = align( text.y + text.height() ); + } else { + text.y = y + (height - text.baseLine()) / 2; + } + } + + public void secondary( String text ) { + secondary.text( text ); + secondary.measure(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/SurfaceScene.java b/src/com/watabou/pixeldungeon/scenes/SurfaceScene.java new file mode 100644 index 0000000000..d707e4d74e --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/SurfaceScene.java @@ -0,0 +1,361 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import java.nio.FloatBuffer; + +import com.watabou.gltextures.Gradient; +import com.watabou.gltextures.SmartTexture; +import com.watabou.glwrap.Matrix; +import com.watabou.glwrap.Quad; +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.Camera; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.noosa.NoosaScript; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.TouchArea; +import com.watabou.noosa.Visual; +import com.watabou.noosa.audio.Music; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.HeroClass; +import com.watabou.pixeldungeon.sprites.RatSprite; +import com.watabou.pixeldungeon.ui.Archs; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class SurfaceScene extends PixelScene { + + private static final int WIDTH = 80; + private static final int HEIGHT = 112; + + private static final int NSTARS = 100; + private static final int NCLOUDS = 5; + + private Camera viewport; + @Override + public void create() { + + super.create(); + + Music.INSTANCE.play( Assets.HAPPY, true ); + Music.INSTANCE.volume( 1f ); + + uiCamera.visible = false; + + int w = Camera.main.width; + int h = Camera.main.height; + + Archs archs = new Archs(); + archs.reversed = true; + archs.setSize( w, h ); + add( archs ); + + float vx = align( (w - WIDTH) / 2 ); + float vy = align( (h - HEIGHT) / 2 ); + + Point s = Camera.main.cameraToScreen( vx, vy ); + viewport = new Camera( s.x, s.y, WIDTH, HEIGHT, defaultZoom ); + Camera.add( viewport ); + + Group window = new Group(); + window.camera = viewport; + add( window ); + + boolean dayTime = !Dungeon.nightMode; + + Sky sky = new Sky( dayTime ); + sky.scale.set( WIDTH, HEIGHT ); + window.add( sky ); + + if (!dayTime) { + for (int i=0; i < NSTARS; i++) { + float size = Random.Float(); + ColorBlock star = new ColorBlock( size, size, 0xFFFFFFFF ); + star.x = Random.Float( WIDTH ) - size / 2; + star.y = Random.Float( HEIGHT ) - size / 2; + star.am = size * (1 - star.y / HEIGHT); + window.add( star ); + } + } + + float range = HEIGHT * 2 / 3; + for (int i=0; i < NCLOUDS; i++) { + Cloud cloud = new Cloud( (NCLOUDS - 1 - i) * (range / NCLOUDS) + Random.Float( range / NCLOUDS ), dayTime ); + window.add( cloud ); + } + + int nPatches = (int)(sky.width() / GrassPatch.WIDTH + 1); + + for (int i=0; i < nPatches * 4; i++) { + GrassPatch patch = new GrassPatch( (i - 0.75f) * GrassPatch.WIDTH / 4, HEIGHT + 1, dayTime ); + patch.brightness( dayTime ? 0.7f : 0.4f ); + window.add( patch ); + } + + Avatar a = new Avatar( Dungeon.hero.heroClass ); + // Removing semitransparent contour + a.am = 2; a.aa = -1; + a.x = PixelScene.align( (WIDTH - a.width) / 2 ); + a.y = HEIGHT - a.height + 1; + window.add( a ); + + final Pet pet = new Pet(); + pet.rm = pet.gm = pet.bm = 1.2f; + pet.x = WIDTH / 2 + 2; + pet.y = HEIGHT - pet.height; + window.add( pet ); + + if (dayTime) { + a.brightness( 1.2f ); + pet.brightness( 1.2f ); + } + + window.add( new TouchArea( sky ) { + protected void onClick( Touch touch ) { + pet.jump(); + }; + } ); + + for (int i=0; i < nPatches; i++) { + GrassPatch patch = new GrassPatch( (i - 0.5f) * GrassPatch.WIDTH, HEIGHT, dayTime ); + patch.brightness( dayTime ? 1.0f : 0.8f ); + window.add( patch ); + } + + Image frame = new Image( Assets.SURFACE ); + if (!dayTime) { + frame.hardlight( 0xDDEEFF ); + } + frame.frame( 0, 0, 88, 125 ); + frame.x = vx - 4; + frame.y = vy - 9; + add( frame ); + + RedButton gameOver = new RedButton( "Game Over" ) { + protected void onClick() { + Game.switchScene( TitleScene.class ); + } + }; + gameOver.setSize( WIDTH - 10, 20 ); + gameOver.setPos( 5 + frame.x + 4, frame.y + frame.height + 4 ); + add( gameOver ); + + Badges.validateHappyEnd(); + + fadeIn(); + } + + @Override + public void destroy() { + Camera.remove( viewport ); + super.destroy(); + } + + @Override + protected void onBackPressed() { + } + + private static class Sky extends Visual { + + private static final int[] day = {0xFF4488FF, 0xFFCCEEFF}; + private static final int[] night = {0xFF001155, 0xFF335980}; + + private SmartTexture texture; + private FloatBuffer verticesBuffer; + + public Sky( boolean dayTime ) { + super( 0, 0, 1, 1 ); + + texture = new Gradient( dayTime ? day : night ); + + float[] vertices = new float[16]; + verticesBuffer = Quad.create(); + + vertices[2] = 0.25f; + vertices[6] = 0.25f; + vertices[10] = 0.75f; + vertices[14] = 0.75f; + + vertices[3] = 0; + vertices[7] = 1; + vertices[11] = 1; + vertices[15] = 0; + + + vertices[0] = 0; + vertices[1] = 0; + + vertices[4] = 1; + vertices[5] = 0; + + vertices[8] = 1; + vertices[9] = 1; + + vertices[12] = 0; + vertices[13] = 1; + + verticesBuffer.position( 0 ); + verticesBuffer.put( vertices ); + } + + @Override + public void draw() { + + super.draw(); + + NoosaScript script = NoosaScript.get(); + + texture.bind(); + + script.camera( camera() ); + + script.uModel.valueM4( matrix ); + script.lighting( + rm, gm, bm, am, + ra, ga, ba, aa ); + + script.drawQuad( verticesBuffer ); + } + } + + private static class Cloud extends Image { + + private static int lastIndex = -1; + + public Cloud( float y, boolean dayTime ) { + super( Assets.SURFACE ); + + int index; + do { + index = Random.Int( 3 ); + } while (index == lastIndex); + + switch (index) { + case 0: + frame( 88, 0, 49, 20 ); + break; + case 1: + frame( 88, 20, 49, 22 ); + break; + case 2: + frame( 88, 42, 50, 18 ); + break; + } + + lastIndex = index; + + this.y = y; + + scale.set( 1 - y / HEIGHT ); + x = Random.Float( WIDTH + width() ) - width(); + speed.x = scale.x * (dayTime ? +8 : -8); + + if (dayTime) { + tint( 0xCCEEFF, 1 - scale.y ); + } else { + rm = gm = bm = +3.0f; + ra = ga = ba = -2.1f; + } + } + + @Override + public void update() { + super.update(); + if (speed.x > 0 && x > WIDTH) { + x = -width(); + } else if (speed.x < 0 && x < -width()) { + x = WIDTH; + } + } + } + + private static class Avatar extends Image { + + private static final int WIDTH = 24; + private static final int HEIGHT = 32; + + public Avatar( HeroClass cl ) { + super( Assets.AVATARS ); + frame( new TextureFilm( texture, WIDTH, HEIGHT ).get( cl.ordinal() ) ); + } + } + + private static class Pet extends RatSprite { + + public void jump() { + play( run ); + } + + @Override + public void onComplete( Animation anim ) { + if (anim == run) { + idle(); + } + } + } + + private static class GrassPatch extends Image { + + public static final int WIDTH = 16; + public static final int HEIGHT = 14; + + private float tx; + private float ty; + + private double a = Random.Float( 5 ); + private double angle; + + private boolean forward; + + public GrassPatch( float tx, float ty, boolean forward ) { + + super( Assets.SURFACE ); + + frame( 88 + Random.Int( 4 ) * WIDTH, 60, WIDTH, HEIGHT ); + + this.tx = tx; + this.ty = ty; + + this.forward = forward; + } + + @Override + public void update() { + super.update(); + a += Random.Float( Game.elapsed * 5 ); + angle = (2 + Math.cos( a )) * (forward ? +0.2 : -0.2); + + scale.y = (float)Math.cos( angle ); + + x = tx + (float)Math.tan( angle ) * width; + y = ty - scale.y * height; + } + + @Override + protected void updateMatrix() { + super.updateMatrix(); + Matrix.skewX( matrix, (float)(angle / Matrix.G2RAD) ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/scenes/TitleScene.java b/src/com/watabou/pixeldungeon/scenes/TitleScene.java new file mode 100644 index 0000000000..074a9535fb --- /dev/null +++ b/src/com/watabou/pixeldungeon/scenes/TitleScene.java @@ -0,0 +1,176 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.scenes; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Music; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.BannerSprites; +import com.watabou.pixeldungeon.effects.Fireball; +import com.watabou.pixeldungeon.ui.Archs; +import com.watabou.pixeldungeon.ui.PrefsButton; + +public class TitleScene extends PixelScene { + + private static final String TXT_PLAY = "Play"; + private static final String TXT_HIGHSCORES = "Rankings"; + private static final String TXT_BADGES = "Badges"; + private static final String TXT_ABOUT = "About"; + + @Override + public void create() { + + super.create(); + + Music.INSTANCE.play( Assets.THEME, true ); + Music.INSTANCE.volume( 1f ); + + uiCamera.visible = false; + + int w = Camera.main.width; + int h = Camera.main.height; + + float height = 180; + + Archs archs = new Archs(); + archs.setSize( w, h ); + add( archs ); + + Image title = BannerSprites.get( BannerSprites.Type.PIXEL_DUNGEON ); + add( title ); + + title.x = (w - title.width()) / 2; + title.y = (h - height) / 2; + + placeTorch( title.x + 18, title.y + 20 ); + placeTorch( title.x + title.width - 18, title.y + 20 ); + + DashboardItem btnBadges = new DashboardItem( TXT_BADGES, 3 ) { + @Override + protected void onClick() { + Game.switchScene( BadgesScene.class ); + } + }; + btnBadges.setPos( w / 2 - btnBadges.width(), (h + height) / 2 - DashboardItem.SIZE ); + add( btnBadges ); + + DashboardItem btnAbout = new DashboardItem( TXT_ABOUT, 1 ) { + @Override + protected void onClick() { + Game.switchScene( AboutScene.class ); + } + }; + btnAbout.setPos( w / 2, (h + height) / 2 - DashboardItem.SIZE ); + add( btnAbout ); + + DashboardItem btnPlay = new DashboardItem( TXT_PLAY, 0 ) { + @Override + protected void onClick() { + Game.switchScene( StartScene.class ); + } + }; + btnPlay.setPos( w / 2 - btnPlay.width(), btnAbout.top() - DashboardItem.SIZE ); + add( btnPlay ); + + DashboardItem btnHighscores = new DashboardItem( TXT_HIGHSCORES, 2 ) { + @Override + protected void onClick() { + Game.switchScene( RankingsScene.class ); + } + }; + btnHighscores.setPos( w / 2, btnPlay.top() ); + add( btnHighscores ); + + BitmapText version = new BitmapText( "v " + Game.version, font1x ); + version.measure(); + version.hardlight( 0x888888 ); + version.x = w - version.width(); + version.y = h - version.height(); + add( version ); + + PrefsButton btnPrefs = new PrefsButton(); + btnPrefs.setPos( w - btnPrefs.width() - 1, 1 ); + add( btnPrefs ); + + fadeIn(); + } + + private void placeTorch( float x, float y ) { + Fireball fb = new Fireball(); + fb.setPos( x, y ); + add( fb ); + } + + private static class DashboardItem extends Button { + + public static final float SIZE = 48; + + private static final int IMAGE_SIZE = 32; + + private Image image; + private BitmapText label; + + public DashboardItem( String text, int index ) { + super(); + + image.frame( image.texture.uvRect( index * IMAGE_SIZE, 0, (index + 1) * IMAGE_SIZE, IMAGE_SIZE ) ); + this.label.text( text ); + this.label.measure(); + + setSize( SIZE, SIZE ); + } + + @Override + protected void createChildren() { + super.createChildren(); + + image = new Image( Assets.DASHBOARD ); + add( image ); + + label = createText( 9 ); + add( label ); + } + + @Override + protected void layout() { + super.layout(); + + image.x = align( x + (width - image.width()) / 2 ); + image.y = align( y ); + + label.x = align( x + (width - label.width()) / 2 ); + label.y = align( image.y + image.height() +2 ); + } + + @Override + protected void onTouchDown() { + image.brightness( 1.5f ); + Sample.INSTANCE.play( Assets.SND_CLICK, 1, 1, 0.8f ); + } + + @Override + protected void onTouchUp() { + image.resetColor(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/AcidicSprite.java b/src/com/watabou/pixeldungeon/sprites/AcidicSprite.java new file mode 100644 index 0000000000..eeac79c0f1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/AcidicSprite.java @@ -0,0 +1,53 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class AcidicSprite extends ScorpioSprite { + + public AcidicSprite() { + super(); + + texture( Assets.SCORPIO ); + + TextureFilm frames = new TextureFilm( texture, 18, 17 ); + + idle = new Animation( 12, true ); + idle.frames( frames, 14, 14, 14, 14, 14, 14, 14, 14, 15, 16, 15, 16, 15, 16 ); + + run = new Animation( 4, true ); + run.frames( frames, 19, 20 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 14, 17, 18 ); + + zap = attack.clone(); + + die = new Animation( 12, false ); + die.frames( frames, 14, 21, 22, 23, 24 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFF66FF22; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/AlbinoSprite.java b/src/com/watabou/pixeldungeon/sprites/AlbinoSprite.java new file mode 100644 index 0000000000..473dcec543 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/AlbinoSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class AlbinoSprite extends MobSprite { + + public AlbinoSprite() { + super(); + + texture( Assets.RAT ); + + TextureFilm frames = new TextureFilm( texture, 16, 15 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 16, 16, 16, 17 ); + + run = new Animation( 10, true ); + run.frames( frames, 22, 23, 24, 25, 26 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 18, 19, 20, 21 ); + + die = new Animation( 10, false ); + die.frames( frames, 27, 28, 29, 30 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/BanditSprite.java b/src/com/watabou/pixeldungeon/sprites/BanditSprite.java new file mode 100644 index 0000000000..a4d8cc6a52 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/BanditSprite.java @@ -0,0 +1,45 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class BanditSprite extends MobSprite { + + public BanditSprite() { + super(); + + texture( Assets.THIEF ); + TextureFilm film = new TextureFilm( texture, 12, 13 ); + + idle = new Animation( 1, true ); + idle.frames( film, 21, 21, 21, 22, 21, 21, 21, 21, 22 ); + + run = new Animation( 15, true ); + run.frames( film, 21, 21, 23, 24, 24, 25 ); + + die = new Animation( 10, false ); + die.frames( film, 25, 27, 28, 29, 30 ); + + attack = new Animation( 12, false ); + attack.frames( film, 31, 32, 33 ); + + idle(); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/BatSprite.java b/src/com/watabou/pixeldungeon/sprites/BatSprite.java new file mode 100644 index 0000000000..050e139f9f --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/BatSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class BatSprite extends MobSprite { + + public BatSprite() { + super(); + + texture( Assets.BAT ); + + TextureFilm frames = new TextureFilm( texture, 15, 15 ); + + idle = new Animation( 8, true ); + idle.frames( frames, 0, 1 ); + + run = new Animation( 12, true ); + run.frames( frames, 0, 1 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 2, 3, 0, 1 ); + + die = new Animation( 12, false ); + die.frames( frames, 4, 5, 6 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/BlacksmithSprite.java b/src/com/watabou/pixeldungeon/sprites/BlacksmithSprite.java new file mode 100644 index 0000000000..fb8e6be00e --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/BlacksmithSprite.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.levels.Level; + +public class BlacksmithSprite extends MobSprite { + + private Emitter emitter; + + public BlacksmithSprite() { + super(); + + texture( Assets.TROLL ); + + TextureFilm frames = new TextureFilm( texture, 13, 16 ); + + idle = new Animation( 15, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 3 ); + + run = new Animation( 20, true ); + run.frames( frames, 0 ); + + die = new Animation( 20, false ); + die.frames( frames, 0 ); + + play( idle ); + } + + @Override + public void link( Char ch ) { + super.link( ch ); + + emitter = new Emitter(); + emitter.autoKill = false; + emitter.pos( x + 7, y + 12 ); + parent.add( emitter ); + } + + @Override + public void update() { + super.update(); + + if (emitter != null) { + emitter.visible = visible; + } + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim ); + + if (visible && emitter != null && anim == idle) { + emitter.burst( Speck.factory( Speck.FORGE ), 3 ); + float volume = 0.2f / (Level.distance( ch.pos, Dungeon.hero.pos )); + Sample.INSTANCE.play( Assets.SND_EVOKE, volume, volume, 0.8f ); + } + } + +} diff --git a/src/com/watabou/pixeldungeon/sprites/BruteSprite.java b/src/com/watabou/pixeldungeon/sprites/BruteSprite.java new file mode 100644 index 0000000000..90ccc923d7 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/BruteSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class BruteSprite extends MobSprite { + + public BruteSprite() { + super(); + + texture( Assets.BRUTE ); + + TextureFilm frames = new TextureFilm( texture, 12, 16 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1, 0, 0, 1, 1 ); + + run = new Animation( 12, true ); + run.frames( frames, 4, 5, 6, 7 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 2, 3, 0 ); + + die = new Animation( 12, false ); + die.frames( frames, 8, 9, 10 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/BurningFistSprite.java b/src/com/watabou/pixeldungeon/sprites/BurningFistSprite.java new file mode 100644 index 0000000000..ae56f1d792 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/BurningFistSprite.java @@ -0,0 +1,77 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.utils.Callback; + +public class BurningFistSprite extends MobSprite { + + public BurningFistSprite() { + super(); + + texture( Assets.BURNING ); + + TextureFilm frames = new TextureFilm( texture, 24, 17 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 1 ); + + run = new Animation( 3, true ); + run.frames( frames, 0, 1 ); + + attack = new Animation( 8, false ); + attack.frames( frames, 0, 5, 6 ); + + die = new Animation( 10, false ); + die.frames( frames, 0, 2, 3, 4 ); + + play( idle ); + } + + private int posToShoot; + + @Override + public void attack( int cell ) { + posToShoot = cell; + super.attack( cell ); + } + + @Override + public void onComplete( Animation anim ) { + if (anim == attack) { + + Sample.INSTANCE.play( Assets.SND_ZAP ); + MagicMissile.shadow( parent, ch.pos, posToShoot, + new Callback() { + @Override + public void call() { + ch.onAttackComplete(); + } + } ); + + idle(); + + } else { + super.onComplete( anim ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/CharSprite.java b/src/com/watabou/pixeldungeon/sprites/CharSprite.java new file mode 100644 index 0000000000..24981b9a4d --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/CharSprite.java @@ -0,0 +1,407 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.Game; +import com.watabou.noosa.MovieClip; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.tweeners.PosTweener; +import com.watabou.noosa.tweeners.Tweener; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.EmoIcon; +import com.watabou.pixeldungeon.effects.FloatingText; +import com.watabou.pixeldungeon.effects.IceBlock; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.Splash; +import com.watabou.pixeldungeon.effects.TorchHalo; +import com.watabou.pixeldungeon.effects.particles.FlameParticle; +import com.watabou.pixeldungeon.items.potions.PotionOfInvisibility; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Callback; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip.Listener { + + // Color constants for floating text + public static final int DEFAULT = 0xFFFFFF; + public static final int POSITIVE = 0x00FF00; + public static final int NEGATIVE = 0xFF0000; + public static final int WARNING = 0xFF8800; + public static final int NEUTRAL = 0xFFFF00; + + private static final float MOVE_INTERVAL = 0.1f; + private static final float FLASH_INTERVAL = 0.05f; + + public enum State { + BURNING, LEVITATING, INVISIBLE, PARALYSED, FROZEN, ILLUMINATED + } + + protected Animation idle; + protected Animation run; + protected Animation attack; + protected Animation operate; + protected Animation zap; + protected Animation die; + + protected Callback animCallback; + + protected Tweener motion; + + protected Emitter burning; + protected Emitter levitation; + + protected IceBlock iceBlock; + protected TorchHalo halo; + + protected EmoIcon emo; + + private float flashTime = 0; + + protected boolean sleeping = false; + + // Char owner + public Char ch; + + public CharSprite() { + super(); + listener = this; + } + + public void link( Char ch ) { + this.ch = ch; + ch.sprite = this; + + place( ch.pos ); + turnTo( ch.pos, Random.Int( Level.LENGTH ) ); + + ch.updateSpriteState(); + } + + public PointF worldToCamera( int cell ) { + + final int csize = DungeonTilemap.SIZE; + + return new PointF( + ((cell % Level.WIDTH) + 0.5f) * csize - width * 0.5f, + ((cell / Level.WIDTH) + 1.0f) * csize - height + ); + } + + public void place( int cell ) { + point( worldToCamera( cell ) ); + } + + public void showStatus( int color, String text, Object... args ) { + if (visible) { + if (args.length > 0) { + text = Utils.format( text, args ); + } + if (ch != null) { + FloatingText.show( x + width * 0.5f, y, ch.pos, text, color ); + } else { + FloatingText.show( x + width * 0.5f, y, text, color ); + } + } + } + + public void idle() { + play( idle ); + } + + public void move( int from, int to ) { + play( run ); + + motion = new PosTweener( this, worldToCamera( to ), MOVE_INTERVAL ); + motion.listener = this; + parent.add( motion ); + + turnTo( from , to ); + + if (visible && Level.water[from] && !ch.flying) { + GameScene.ripple( from ); + } + } + + public void interruptMotion() { + if (motion != null) { + onComplete( motion ); + } + } + + public void attack( int cell ) { + turnTo( ch.pos, cell ); + play( attack ); + } + + public void attack( int cell, Callback callback ) { + animCallback = callback; + turnTo( ch.pos, cell ); + play( attack ); + } + + public void operate( int cell ) { + turnTo( ch.pos, cell ); + play( operate ); + } + + public void zap( int cell ) { + turnTo( ch.pos, cell ); + play( zap ); + } + + public void turnTo( int from, int to ) { + int fx = from % Level.WIDTH; + int tx = to % Level.WIDTH; + if (tx > fx) { + flipHorizontal = false; + } else if (tx < fx) { + flipHorizontal = true; + } + } + + public void die() { + sleeping = false; + play( die ); + + if (emo != null) { + emo.killAndErase(); + } + } + + public Emitter emitter() { + Emitter emitter = GameScene.emitter(); + emitter.pos( this ); + return emitter; + } + + public Emitter centerEmitter() { + Emitter emitter = GameScene.emitter(); + emitter.pos( center() ); + return emitter; + } + + public Emitter bottomEmitter() { + Emitter emitter = GameScene.emitter(); + emitter.pos( x, y + height, width, 0 ); + return emitter; + } + + public void burst( final int color, int n ) { + if (visible) { + Splash.at( center(), color, n ); + } + } + + public void bloodBurstA( PointF from, int damage ) { + if (visible) { + PointF c = center(); + int n = (int)Math.min( 9 * Math.sqrt( (double)damage / ch.HT ), 9 ); + Splash.at( c, PointF.angle( from, c ), 3.1415926f / 2, blood(), n ); + } + } + + // Blood color + public int blood() { + return 0xFFBB0000; + } + + public void flash() { + ra = ba = ga = 1f; + flashTime = FLASH_INTERVAL; + } + + public void add( State state ) { + switch (state) { + case BURNING: + burning = emitter(); + burning.pour( FlameParticle.FACTORY, 0.06f ); + if (visible) { + Sample.INSTANCE.play( Assets.SND_BURNING ); + } + break; + case LEVITATING: + levitation = emitter(); + levitation.pour( Speck.factory( Speck.JET ), 0.02f ); + break; + case INVISIBLE: + PotionOfInvisibility.melt( ch ); + break; + case PARALYSED: + paused = true; + break; + case FROZEN: + iceBlock = IceBlock.freeze( this ); + paused = true; + break; + case ILLUMINATED: + GameScene.effect( halo = new TorchHalo( this ) ); + break; + } + } + + public void remove( State state ) { + switch (state) { + case BURNING: + if (burning != null) { + burning.on = false; + burning = null; + } + break; + case LEVITATING: + if (levitation != null) { + levitation.on = false; + levitation = null; + } + break; + case INVISIBLE: + alpha( 1f ); + break; + case PARALYSED: + paused = false; + break; + case FROZEN: + if (iceBlock != null) { + iceBlock.melt(); + iceBlock = null; + } + paused = false; + break; + case ILLUMINATED: + if (halo != null) { + halo.putOut(); + } + break; + } + } + + @Override + public void update() { + + super.update(); + + if (paused && listener != null) { + listener.onComplete( curAnim ); + } + + if (flashTime > 0 && (flashTime -= Game.elapsed) <= 0) { + resetColor(); + } + + if (burning != null) { + burning.visible = visible; + } + if (levitation != null) { + levitation.visible = visible; + } + if (iceBlock != null) { + iceBlock.visible = visible; + } + if (sleeping) { + showSleep(); + } else { + hideSleep(); + } + if (emo != null) { + emo.visible = visible; + } + } + + public void showSleep() { + if (emo instanceof EmoIcon.Sleep) { + + } else { + if (emo != null) { + emo.killAndErase(); + } + emo = new EmoIcon.Sleep( this ); + } + } + + public void hideSleep() { + if (emo instanceof EmoIcon.Sleep) { + emo.killAndErase(); + emo = null; + } + } + + public void showAlert() { + if (emo instanceof EmoIcon.Alert) { + + } else { + if (emo != null) { + emo.killAndErase(); + } + emo = new EmoIcon.Alert( this ); + } + } + + public void hideAlert() { + if (emo instanceof EmoIcon.Alert) { + emo.killAndErase(); + emo = null; + } + } + + @Override + public void kill() { + super.kill(); + + if (emo != null) { + emo.killAndErase(); + emo = null; + } + } + + @Override + public void onComplete( Tweener tweener ) { + if (tweener == motion) { + motion.killAndErase(); + motion = null; + ch.onMotionComplete(); + } + } + + @Override + public void onComplete( Animation anim ) { + + if (animCallback != null) { + animCallback.call(); + animCallback = null; + } else { + + if (anim == attack) { + + idle(); + ch.onAttackComplete(); + + } else if (anim == operate) { + + idle(); + ch.onOperateComplete(); + + } + + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/CrabSprite.java b/src/com/watabou/pixeldungeon/sprites/CrabSprite.java new file mode 100644 index 0000000000..f06c2f5189 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/CrabSprite.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class CrabSprite extends MobSprite { + + public CrabSprite() { + super(); + + texture( Assets.CRAB ); + + TextureFilm frames = new TextureFilm( texture, 16 ); + + idle = new Animation( 5, true ); + idle.frames( frames, 0, 1, 0, 2 ); + + run = new Animation( 15, true ); + run.frames( frames, 3, 4, 5, 6 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 7, 8, 9 ); + + die = new Animation( 12, false ); + die.frames( frames, 10, 11, 12, 13 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFFFFEA80; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/DM300Sprite.java b/src/com/watabou/pixeldungeon/sprites/DM300Sprite.java new file mode 100644 index 0000000000..29527ef1f1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/DM300Sprite.java @@ -0,0 +1,62 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.Speck; + +public class DM300Sprite extends MobSprite { + + public DM300Sprite() { + super(); + + texture( Assets.DM300 ); + + TextureFilm frames = new TextureFilm( texture, 22, 20 ); + + idle = new Animation( 10, true ); + idle.frames( frames, 0, 1 ); + + run = new Animation( 10, true ); + run.frames( frames, 2, 3 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 4, 5, 6 ); + + die = new Animation( 20, false ); + die.frames( frames, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 8 ); + + play( idle ); + } + + @Override + public void onComplete( Animation anim ) { + + super.onComplete( anim ); + + if (anim == die) { + emitter().burst( Speck.factory( Speck.WOOL ), 15 ); + } + } + + @Override + public int blood() { + return 0xFFFFFF88; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/DiscardedItemSprite.java b/src/com/watabou/pixeldungeon/sprites/DiscardedItemSprite.java new file mode 100644 index 0000000000..1f14ba904f --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/DiscardedItemSprite.java @@ -0,0 +1,48 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.Game; + +public class DiscardedItemSprite extends ItemSprite { + + public DiscardedItemSprite() { + + super(); + + originToCenter(); + angularSpeed = 720; + } + + @Override + public void drop() { + scale.set( 1 ); + am = 1; + } + + @Override + public void update() { + + super.update(); + + scale.set( scale.x * 0.9f ); + if ((am -= Game.elapsed) <= 0) { + remove(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ElementalSprite.java b/src/com/watabou/pixeldungeon/sprites/ElementalSprite.java new file mode 100644 index 0000000000..a74a358a59 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ElementalSprite.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Char; + +public class ElementalSprite extends MobSprite { + + public ElementalSprite() { + super(); + + texture( Assets.ELEMENTAL ); + + TextureFilm frames = new TextureFilm( texture, 12, 14 ); + + idle = new Animation( 10, true ); + idle.frames( frames, 0, 1, 2 ); + + run = new Animation( 12, true ); + run.frames( frames, 0, 1, 3 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 4, 5, 6 ); + + die = new Animation( 15, false ); + die.frames( frames, 7, 8, 9, 10, 11, 12, 13, 12 ); + + play( idle ); + } + + @Override + public void link( Char ch ) { + super.link( ch ); + add( State.BURNING ); + } + + @Override + public void die() { + super.die(); + remove( State.BURNING ); + } + + @Override + public int blood() { + return 0xFFFF7D13; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/EyeSprite.java b/src/com/watabou/pixeldungeon/sprites/EyeSprite.java new file mode 100644 index 0000000000..f56bd601bf --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/EyeSprite.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.effects.DeathRay; + +public class EyeSprite extends MobSprite { + + private int attackPos; + + public EyeSprite() { + super(); + + texture( Assets.EYE ); + + TextureFilm frames = new TextureFilm( texture, 16, 18 ); + + idle = new Animation( 8, true ); + idle.frames( frames, 0, 1, 2 ); + + run = new Animation( 12, true ); + run.frames( frames, 5, 6 ); + + attack = new Animation( 8, false ); + attack.frames( frames, 4, 3 ); + + die = new Animation( 8, false ); + die.frames( frames, 7, 8, 9 ); + + play( idle ); + } + + @Override + public void attack( int pos ) { + attackPos = pos; + super.attack( pos ); + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim ); + + if (anim == attack) { + if (Dungeon.visible[ch.pos] || Dungeon.visible[attackPos]) { + parent.add( new DeathRay( center(), DungeonTilemap.tileCenterToWorld( attackPos ) ) ); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/FetidRatSprite.java b/src/com/watabou/pixeldungeon/sprites/FetidRatSprite.java new file mode 100644 index 0000000000..3942f4041c --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/FetidRatSprite.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.Speck; + +public class FetidRatSprite extends RatSprite { + + private Emitter cloud; + + @Override + public void link( Char ch ) { + super.link( ch ); + + if (cloud == null) { + cloud = emitter(); + cloud.pour( Speck.factory( Speck.PARALYSIS ), 0.7f ); + } + } + + @Override + public void update() { + + super.update(); + + if (cloud != null) { + cloud.visible = visible; + } + } + + @Override + public void die() { + super.die(); + + if (cloud != null) { + cloud.on = false; + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/GhostSprite.java b/src/com/watabou/pixeldungeon/sprites/GhostSprite.java new file mode 100644 index 0000000000..917f49797e --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/GhostSprite.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ShaftParticle; + +public class GhostSprite extends MobSprite { + + public GhostSprite() { + super(); + + texture( Assets.GHOST ); + + TextureFilm frames = new TextureFilm( texture, 14, 15 ); + + idle = new Animation( 5, true ); + idle.frames( frames, 0, 1 ); + + run = new Animation( 10, true ); + run.frames( frames, 0, 1 ); + + die = new Animation( 20, false ); + die.frames( frames, 0 ); + + play( idle ); + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } + + @Override + public void die() { + super.die(); + emitter().start( ShaftParticle.FACTORY, 0.3f, 4 ); + emitter().start( Speck.factory( Speck.LIGHT ), 0.2f, 3 ); + } + + @Override + public int blood() { + return 0xFFFFFF; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/GnollSprite.java b/src/com/watabou/pixeldungeon/sprites/GnollSprite.java new file mode 100644 index 0000000000..a4bc706bd1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/GnollSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class GnollSprite extends MobSprite { + + public GnollSprite() { + super(); + + texture( Assets.GNOLL ); + + TextureFilm frames = new TextureFilm( texture, 12, 15 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1, 0, 0, 1, 1 ); + + run = new Animation( 12, true ); + run.frames( frames, 4, 5, 6, 7 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 2, 3, 0 ); + + die = new Animation( 12, false ); + die.frames( frames, 8, 9, 10 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/GolemSprite.java b/src/com/watabou/pixeldungeon/sprites/GolemSprite.java new file mode 100644 index 0000000000..7179e9d724 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/GolemSprite.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; + +public class GolemSprite extends MobSprite { + + public GolemSprite() { + super(); + + texture( Assets.GOLEM ); + + TextureFilm frames = new TextureFilm( texture, 16, 16 ); + + idle = new Animation( 4, true ); + idle.frames( frames, 0, 1 ); + + run = new Animation( 12, true ); + run.frames( frames, 2, 3, 4, 5 ); + + attack = new Animation( 10, false ); + attack.frames( frames, 6, 7, 8 ); + + die = new Animation( 15, false ); + die.frames( frames, 9, 10, 11, 12, 13 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFF80706c; + } + + @Override + public void onComplete( Animation anim ) { + if (anim == die) { + emitter().burst( ElmoParticle.FACTORY, 4 ); + } + super.onComplete( anim ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/GooSprite.java b/src/com/watabou/pixeldungeon/sprites/GooSprite.java new file mode 100644 index 0000000000..ae730e8af2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/GooSprite.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class GooSprite extends MobSprite { + + private Animation pump; + + public GooSprite() { + super(); + + texture( Assets.GOO ); + + TextureFilm frames = new TextureFilm( texture, 20, 14 ); + + idle = new Animation( 10, true ); + idle.frames( frames, 0, 1 ); + + run = new Animation( 10, true ); + run.frames( frames, 0, 1 ); + + pump = new Animation( 20, true ); + pump.frames( frames, 0, 1 ); + + attack = new Animation( 10, false ); + attack.frames( frames, 5, 0, 6 ); + + die = new Animation( 10, false ); + die.frames( frames, 2, 3, 4 ); + + play( idle ); + } + + public void pumpUp() { + play( pump ); + } + + @Override + public int blood() { + return 0xFF000000; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/HeroSprite.java b/src/com/watabou/pixeldungeon/sprites/HeroSprite.java new file mode 100644 index 0000000000..c7ae8c9383 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/HeroSprite.java @@ -0,0 +1,127 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import android.graphics.RectF; + +import com.watabou.gltextures.SmartTexture; +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.hero.HeroClass; + +public class HeroSprite extends CharSprite { + + private static final int FRAME_WIDTH = 12; + private static final int FRAME_HEIGHT = 15; + + private static final int RUN_FRAMERATE = 20; + + private static TextureFilm tiers; + + private Animation fly; + + public HeroSprite() { + super(); + + link( Dungeon.hero ); + + texture( Dungeon.hero.heroClass.spritesheet() ); + updateArmor(); + + idle(); + } + + public void updateArmor() { + + TextureFilm film = new TextureFilm( tiers(), ((Hero)ch).tier(), FRAME_WIDTH, FRAME_HEIGHT ); + + idle = new Animation( 1, true ); + idle.frames( film, 0, 0, 0, 1, 0, 0, 1, 1 ); + + run = new Animation( RUN_FRAMERATE, true ); + run.frames( film, 2, 3, 4, 5, 6, 7 ); + + die = new Animation( 20, false ); + die.frames( film, 8, 9, 10, 11, 12, 11 ); + + attack = new Animation( 15, false ); + attack.frames( film, 13, 14, 15, 0 ); + + zap = attack.clone(); + + operate = new Animation( 8, false ); + operate.frames( film, 16, 17, 16, 17 ); + + fly = new Animation( 1, true ); + fly.frames( film, 18 ); + } + + @Override + public void place( int p ) { + super.place( p ); + Camera.main.target = this; + } + + @Override + public void move( int from, int to ) { + super.move( from, to ); + if (ch.flying) { + play( fly ); + } + Camera.main.target = this; + } + + @Override + public void update() { + sleeping = ((Hero)ch).restoreHealth; + + super.update(); + } + + public boolean sprint( boolean on ) { + run.delay = on ? 0.625f / RUN_FRAMERATE : 1f / RUN_FRAMERATE; + return on; + } + + public static TextureFilm tiers() { + if (tiers == null) { + // Sprites for all classes are the same in size + SmartTexture texture = TextureCache.get( Assets.ROGUE ); + tiers = new TextureFilm( texture, texture.width, FRAME_HEIGHT ); + } + + return tiers; + } + + public static Image avatar( HeroClass cl, int armorTier ) { + + RectF patch = tiers().get( armorTier ); + Image avatar = new Image( cl.spritesheet() ); + RectF frame = avatar.texture.uvRect( 1, 0, FRAME_WIDTH, FRAME_HEIGHT ); + frame.offset( patch.left, patch.top ); + avatar.frame( frame ); + + return avatar; + } + +} diff --git a/src/com/watabou/pixeldungeon/sprites/ImpSprite.java b/src/com/watabou/pixeldungeon/sprites/ImpSprite.java new file mode 100644 index 0000000000..abd6edebb6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ImpSprite.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.effects.Speck; + +public class ImpSprite extends MobSprite { + + public ImpSprite() { + super(); + + texture( Assets.IMP ); + + TextureFilm frames = new TextureFilm( texture, 12, 14 ); + + idle = new Animation( 10, true ); + idle.frames( frames, + 0, 1, 2, 3, 0, 1, 2, 3, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 3, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4 ); + + run = new Animation( 20, true ); + run.frames( frames, 0 ); + + die = new Animation( 10, false ); + die.frames( frames, 0, 3, 2, 1, 0, 3, 2, 1, 0 ); + + play( idle ); + } + + @Override + public void link( Char ch ) { + super.link( ch ); + + if (ch instanceof Imp) { + alpha( 0.4f ); + } + } + + @Override + public void onComplete( Animation anim ) { + if (anim == die) { + + emitter().burst( Speck.factory( Speck.WOOL ), 15 ); + killAndErase(); + + } else { + super.onComplete( anim ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ItemSprite.java b/src/com/watabou/pixeldungeon/sprites/ItemSprite.java new file mode 100644 index 0000000000..e4597fac92 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ItemSprite.java @@ -0,0 +1,224 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import android.graphics.Bitmap; + +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.Game; +import com.watabou.noosa.MovieClip; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.effects.CellEmitter; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class ItemSprite extends MovieClip { + + public static final int SIZE = 16; + + private static final float DROP_INTERVAL = 0.4f; + + protected static TextureFilm film; + + public Heap heap; + + private Glowing glowing; + private float phase; + private boolean glowUp; + + private float dropInterval; + + public ItemSprite() { + this( ItemSpriteSheet.SMTH, null ); + } + + public ItemSprite( Item item ) { + this( item.image(), item.glowing() ); + } + + public ItemSprite( int image, Glowing glowing ) { + super( Assets.ITEMS ); + + if (film == null) { + film = new TextureFilm( texture, SIZE, SIZE ); + } + + view( image, glowing ); + } + + public void originToCenter() { + origin.set(SIZE / 2 ); + } + + public void link() { + link( heap ); + } + + public void link( Heap heap ) { + this.heap = heap; + view( heap.image(), heap.glowing() ); + place( heap.pos ); + } + + @Override + public void revive() { + super.revive(); + + speed.set( 0 ); + acc.set( 0 ); + dropInterval = 0; + + heap = null; + } + + public PointF worldToCamera( int cell ) { + final int csize = DungeonTilemap.SIZE; + + return new PointF( + cell % Level.WIDTH * csize + (csize - SIZE) * 0.5f, + cell / Level.WIDTH * csize + (csize - SIZE) * 0.5f + ); + } + + public void place( int p ) { + point( worldToCamera( p ) ); + } + + public void drop() { + + if (heap.isEmpty()) { + return; + } + + dropInterval = DROP_INTERVAL; + + speed.set( 0, -100 ); + acc.set( 0, -speed.y / DROP_INTERVAL * 2 ); + + if (visible && heap != null && heap.peek() instanceof Gold) { + CellEmitter.center( heap.pos ).burst( Speck.factory( Speck.COIN ), 5 ); + Sample.INSTANCE.play( Assets.SND_GOLD, 1, 1, Random.Float( 0.9f, 1.1f ) ); + } + } + + public void drop( int from ) { + + if (heap.pos == from) { + drop(); + } else { + + float px = x; + float py = y; + drop(); + + place( from ); + + speed.offset( (px-x) / DROP_INTERVAL, (py-y) / DROP_INTERVAL ); + } + } + + public ItemSprite view( int image, Glowing glowing ) { + frame( film.get( image ) ); + if ((this.glowing = glowing) == null) { + resetColor(); + } + return this; + } + + @Override + public void update() { + super.update(); + + // Visibility + visible = heap == null || Dungeon.visible[heap.pos]; + + // Dropping + if (dropInterval > 0 && (dropInterval -= Game.elapsed) <= 0) { + + speed.set( 0 ); + acc.set( 0 ); + place( heap.pos ); + + if (Level.water[heap.pos]) { + GameScene.ripple( heap.pos ); + } + } + + // Glowing + if (visible && glowing != null) { + if (glowUp && (phase += Game.elapsed) > glowing.period) { + + glowUp = false; + phase = glowing.period; + + } else if (!glowUp && (phase -= Game.elapsed) < 0) { + + glowUp = true; + phase = 0; + + } + + float value = phase / glowing.period * 0.6f; + + rm = gm = bm = 1 - value; + ra = glowing.red * value; + ga = glowing.green * value; + ba = glowing.blue * value; + } + } + + public static int pick( int index, int x, int y ) { + Bitmap bmp = TextureCache.get( Assets.ITEMS ).bitmap; + int rows = bmp.getWidth() / SIZE; + int row = index / rows; + int col = index % rows; + return bmp.getPixel( col * SIZE + x, row * SIZE + y ); + } + + public static class Glowing { + + public static final Glowing WHITE = new Glowing( 0xFFFFFF, 0.6f ); + + public float red; + public float green; + public float blue; + public float period; + + public Glowing( int color ) { + this( color, 1f ); + } + + public Glowing( int color, float period ) { + red = (color >> 16) / 255f; + green = ((color >> 8) & 0xFF) / 255f; + blue = (color & 0xFF) / 255f; + + this.period = period; + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ItemSpriteSheet.java b/src/com/watabou/pixeldungeon/sprites/ItemSpriteSheet.java new file mode 100644 index 0000000000..f0b5a5139f --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ItemSpriteSheet.java @@ -0,0 +1,172 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +public class ItemSpriteSheet { + + // Placeholders + public static final int WEAPON = 5; + public static final int ARMOR = 6; + public static final int RING = 7; + public static final int SMTH = 127; + + // Keys + public static final int SKELETON_KEY = 8; + public static final int IRON_KEY = 9; + public static final int GOLDEN_KEY = 10; + + // Melee weapons + public static final int SHORT_SWORD = 2; + public static final int KNUCKLEDUSTER = 16; + public static final int QUARTERSTAFF = 17; + public static final int MACE = 18; + public static final int DAGGER = 19; + public static final int SWORD = 20; + public static final int LONG_SWORD = 21; + public static final int BATTLE_AXE = 22; + public static final int WAR_HAMMER = 23; + public static final int SPEAR = 29; + public static final int GLAIVE = 30; + + // Missile weapons + public static final int SHURIKEN = 15; + public static final int DART = 31; + public static final int BOOMERANG = 106; + public static final int TOMAHAWK = 107; + public static final int INCENDIARY_DART = 108; + public static final int CURARE_DART = 109; + public static final int JAVELIN = 110; + + // Armors + public static final int ARMOR_CLOTH = 24; + public static final int ARMOR_LEATHER = 25; + public static final int ARMOR_MAIL = 26; + public static final int ARMOR_SCALE = 27; + public static final int ARMOR_PLATE = 28; + public static final int ARMOR_ROGUE = 96; + public static final int ARMOR_WARRIOR = 97; + public static final int ARMOR_MAGE = 98; + public static final int ARMOR_HUNTRESS = 99; + + // Wands + public static final int WAND_MAGIC_MISSILE = 3; + public static final int WAND_HOLLY = 48; + public static final int WAND_YEW = 49; + public static final int WAND_EBONY = 50; + public static final int WAND_CHERRY = 51; + public static final int WAND_TEAK = 52; + public static final int WAND_ROWAN = 53; + public static final int WAND_WILLOW = 54; + public static final int WAND_MAHOGANY = 55; + public static final int WAND_BAMBOO = 68; + public static final int WAND_PURPLEHEART = 69; + public static final int WAND_OAK = 70; + public static final int WAND_BIRCH = 71; + + // Rings + public static final int RING_DIAMOND = 32; + public static final int RING_OPAL = 33; + public static final int RING_GARNET = 34; + public static final int RING_RUBY = 35; + public static final int RING_AMETHYST = 36; + public static final int RING_TOPAZ = 37; + public static final int RING_ONYX = 38; + public static final int RING_TOURMALINE = 39; + public static final int RING_EMERALD = 72; + public static final int RING_SAPPHIRE = 73; + public static final int RING_QUARTZ = 74; + public static final int RING_AGATE = 75; + + // Potions + public static final int POTION_TURQUOISE = 56; + public static final int POTION_CRIMSON = 57; + public static final int POTION_AZURE = 58; + public static final int POTION_JADE = 59; + public static final int POTION_GOLDEN = 60; + public static final int POTION_MAGENTA = 61; + public static final int POTION_CHARCOAL = 62; + public static final int POTION_IVORY = 63; + public static final int POTION_AMBER = 64; + public static final int POTION_BISTRE = 65; + public static final int POTION_INDIGO = 66; + public static final int POTION_SILVER = 67; + + // Scrolls + public static final int SCROLL_KAUNAN = 40; + public static final int SCROLL_SOWILO = 41; + public static final int SCROLL_LAGUZ = 42; + public static final int SCROLL_YNGVI = 43; + public static final int SCROLL_GYFU = 44; + public static final int SCROLL_RAIDO = 45; + public static final int SCROLL_ISAZ = 46; + public static final int SCROLL_MANNAZ = 47; + public static final int SCROLL_NAUDIZ = 76; + public static final int SCROLL_BERKANAN = 77; + public static final int SCROLL_ODAL = 78; + public static final int SCROLL_TIWAZ = 79; + + // Seeds + public static final int SEED_FIREBLOOM = 88; + public static final int SEED_ICECAP = 89; + public static final int SEED_SORROWMOSS = 90; + public static final int SEED_BLINDWEED = 91; + public static final int SEED_SUNGRASS = 92; + public static final int SEED_EARTHROOT = 93; + public static final int SEED_FADELEAF = 94; + public static final int SEED_ROTBERRY = 95; + + // Quest items + public static final int ROSE = 100; + public static final int PICKAXE = 101; + public static final int ORE = 102; + public static final int SKULL = 103; + public static final int DUST = 121; + public static final int TOKEN = 122; + + // Heaps (containers) + public static final int BONES = 0; + public static final int CHEST = 11; + public static final int LOCKED_CHEST = 12; + public static final int TOMB = 13; + public static final int CRYSTAL_CHEST = 105; + + // Food + public static final int RATION = 4; + public static final int PASTY = 112; + public static final int MEAT = 113; + public static final int STEAK = 114; + public static final int OVERPRICED = 115; + public static final int CARPACCIO = 116; + + // Bags + public static final int POUCH = 83; + public static final int HOLDER = 104; + public static final int HOLSTER = 111; + + // Misc + public static final int ANKH = 1; + public static final int GOLD = 14; + public static final int STYLUS = 80; + public static final int DEWDROP = 81; + public static final int MASTERY = 82; + public static final int TORCH = 84; + public static final int BEACON = 85; + public static final int KIT = 86; + public static final int AMULET = 87; + public static final int VIAL = 120; +} diff --git a/src/com/watabou/pixeldungeon/sprites/KingSprite.java b/src/com/watabou/pixeldungeon/sprites/KingSprite.java new file mode 100644 index 0000000000..30b45aea9f --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/KingSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class KingSprite extends MobSprite { + + public KingSprite() { + super(); + + texture( Assets.KING ); + + TextureFilm frames = new TextureFilm( texture, 16, 16 ); + + idle = new Animation( 12, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2 ); + + run = new Animation( 15, true ); + run.frames( frames, 3, 4, 5, 6, 7, 8 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 9, 10, 11 ); + + die = new Animation( 8, false ); + die.frames( frames, 12, 13, 14, 15 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/LarvaSprite.java b/src/com/watabou/pixeldungeon/sprites/LarvaSprite.java new file mode 100644 index 0000000000..bb11bd44d2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/LarvaSprite.java @@ -0,0 +1,58 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.Splash; + +public class LarvaSprite extends MobSprite { + + public LarvaSprite() { + super(); + + texture( Assets.LARVA ); + + TextureFilm frames = new TextureFilm( texture, 12, 8 ); + + idle = new Animation( 5, true ); + idle.frames( frames, 4, 4, 4, 4, 4, 5, 5 ); + + run = new Animation( 12, true ); + run.frames( frames, 0, 1, 2, 3 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 6, 5, 7 ); + + die = new Animation( 10, false ); + die.frames( frames, 8 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xbbcc66; + } + + @Override + public void die() { + Splash.at( center(), blood(), 10 ); + super.die(); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/MirrorSprite.java b/src/com/watabou/pixeldungeon/sprites/MirrorSprite.java new file mode 100644 index 0000000000..0f9335a61d --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/MirrorSprite.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.npcs.MirrorImage; + +public class MirrorSprite extends MobSprite { + + private static final int FRAME_WIDTH = 12; + private static final int FRAME_HEIGHT = 15; + + public MirrorSprite() { + super(); + + texture( Dungeon.hero.heroClass.spritesheet() ); + updateArmor( 0 ); + idle(); + } + + @Override + public void link( Char ch ) { + super.link( ch ); + updateArmor( ((MirrorImage)ch).tier ); + } + + public void updateArmor( int tier ) { + TextureFilm film = new TextureFilm( HeroSprite.tiers(), tier, FRAME_WIDTH, FRAME_HEIGHT ); + + idle = new Animation( 1, true ); + idle.frames( film, 0, 0, 0, 1, 0, 0, 1, 1 ); + + run = new Animation( 20, true ); + run.frames( film, 2, 3, 4, 5, 6, 7 ); + + die = new Animation( 20, false ); + die.frames( film, 0 ); + + attack = new Animation( 15, false ); + attack.frames( film, 13, 14, 15, 0 ); + + idle(); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/MissileSprite.java b/src/com/watabou/pixeldungeon/sprites/MissileSprite.java new file mode 100644 index 0000000000..2b3f46044d --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/MissileSprite.java @@ -0,0 +1,78 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.tweeners.PosTweener; +import com.watabou.noosa.tweeners.Tweener; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.utils.Callback; +import com.watabou.utils.PointF; + +public class MissileSprite extends ItemSprite implements Tweener.Listener { + + private static final float SPEED = 240f; + + private Callback callback; + + public MissileSprite() { + super(); + originToCenter(); + } + + public void reset( int from, int to, Item item, Callback listener ) { + if (item == null) { + reset( from, to, 0, null, listener ); + } else { + reset( from, to, item.image(), item.glowing(), listener ); + } + } + + public void reset( int from, int to, int image, Glowing glowing, Callback listener ) { + revive(); + + view( image, glowing ); + + this.callback = listener; + + point( DungeonTilemap.tileToWorld( from ) ); + PointF dest = DungeonTilemap.tileToWorld( to ); + + PointF d = PointF.diff( dest, point() ); + speed.set( d ).normalize().scale( SPEED ); + + if (image == 31 || image == 108 || image == 109 || image == 110) { + angularSpeed = 0; + angle = 135 - (float)(Math.atan2( d.x, d.y ) / 3.1415926 * 180); + } else { + angularSpeed = image == 15 || image == 106 ? 1440 : 720; + } + + PosTweener tweener = new PosTweener( this, dest, d.length() / SPEED ); + tweener.listener = this; + parent.add( tweener ); + } + + @Override + public void onComplete( Tweener tweener ) { + kill(); + if (callback != null) { + callback.call(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/MobSprite.java b/src/com/watabou/pixeldungeon/sprites/MobSprite.java new file mode 100644 index 0000000000..bbe59b0383 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/MobSprite.java @@ -0,0 +1,73 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.noosa.tweeners.ScaleTweener; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; + +public class MobSprite extends CharSprite { + + private static final float FADE_TIME = 3f; + private static final float FALL_TIME = 1f; + + @Override + public void update() { + sleeping = ch != null && ((Mob)ch).state == Mob.State.SLEEPING; + + super.update(); + } + + @Override + public void onComplete( Animation anim ) { + + super.onComplete( anim ); + + if (anim == die) { + parent.add( new AlphaTweener( this, 0, FADE_TIME ) { + @Override + protected void onComplete() { + MobSprite.this.killAndErase(); + parent.erase( this ); + }; + } ); + } + } + + public void fall() { + + origin.set( width / 2, height - DungeonTilemap.SIZE / 2 ); + angularSpeed = Random.Int( 2 ) == 0 ? -720 : 720; + + parent.add( new ScaleTweener( this, new PointF( 0, 0 ), FALL_TIME ) { + @Override + protected void onComplete() { + MobSprite.this.killAndErase(); + parent.erase( this ); + }; + @Override + protected void updateValues( float progress ) { + super.updateValues( progress ); + am = 1 - progress; + } + } ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/MonkSprite.java b/src/com/watabou/pixeldungeon/sprites/MonkSprite.java new file mode 100644 index 0000000000..6dedb1dc71 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/MonkSprite.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.Random; + +public class MonkSprite extends MobSprite { + + private Animation kick; + + public MonkSprite() { + super(); + + texture( Assets.MONK ); + + TextureFilm frames = new TextureFilm( texture, 15, 14 ); + + idle = new Animation( 6, true ); + idle.frames( frames, 1, 0, 1, 2 ); + + run = new Animation( 15, true ); + run.frames( frames, 11, 12, 13, 14, 15, 16 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 3, 4, 3, 4 ); + + kick = new Animation( 10, false ); + kick.frames( frames, 5, 6, 5 ); + + die = new Animation( 15, false ); + die.frames( frames, 1, 7, 8, 8, 9, 10 ); + + play( idle ); + } + + @Override + public void attack( int cell ) { + super.attack( cell ); + if (Random.Float() < 0.5f) { + play( kick ); + } + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim == kick ? attack : anim ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/PiranhaSprite.java b/src/com/watabou/pixeldungeon/sprites/PiranhaSprite.java new file mode 100644 index 0000000000..77d128577d --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/PiranhaSprite.java @@ -0,0 +1,56 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.scenes.GameScene; + +public class PiranhaSprite extends MobSprite { + + public PiranhaSprite() { + super(); + + texture( Assets.PIRANHA ); + + TextureFilm frames = new TextureFilm( texture, 12, 16 ); + + idle = new Animation( 8, true ); + idle.frames( frames, 0, 1, 2, 1 ); + + run = new Animation( 20, true ); + run.frames( frames, 0, 1, 2, 1 ); + + attack = new Animation( 20, false ); + attack.frames( frames, 3, 4, 5, 6, 7, 8, 9, 10, 11 ); + + die = new Animation( 4, false ); + die.frames( frames, 12, 13, 14 ); + + play( idle ); + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim ); + + if (anim == attack) { + GameScene.ripple( ch.pos ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/PlantSprite.java b/src/com/watabou/pixeldungeon/sprites/PlantSprite.java new file mode 100644 index 0000000000..e4acaa4bef --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/PlantSprite.java @@ -0,0 +1,110 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.plants.Plant; + +public class PlantSprite extends Image { + + private static final float DELAY = 0.2f; + + private static enum State { + GROWING, NORMAL, WITHERING + } + private State state = State.NORMAL; + private float time; + + private static TextureFilm frames; + + private int pos = -1; + + public PlantSprite() { + super( Assets.PLANTS ); + + if (frames == null) { + // Hardcoded size + frames = new TextureFilm( texture, 16, 16 ); + } + + // Hardcoded origin + origin.set( 8, 12 ); + } + + public PlantSprite( int image ) { + this(); + reset( image ); + } + + public void reset( Plant plant ) { + + revive(); + + reset( plant.image ); + alpha( 1f ); + + pos = plant.pos; + x = pos % Level.WIDTH * DungeonTilemap.SIZE; + y = pos / Level.WIDTH * DungeonTilemap.SIZE; + + state = State.GROWING; + time = DELAY; + } + + public void reset( int image ) { + frame( frames.get( image ) ); + } + + @Override + public void update() { + super.update(); + + visible = pos == -1 || Dungeon.visible[pos]; + + switch (state) { + case GROWING: + if ((time -= Game.elapsed) <= 0) { + state = State.NORMAL; + scale.set( 1 ); + } else { + scale.set( 1 - time / DELAY ); + } + break; + case WITHERING: + if ((time -= Game.elapsed) <= 0) { + super.kill(); + } else { + alpha( time / DELAY ); + } + break; + default: + } + } + + @Override + public void kill() { + state = State.WITHERING; + time = DELAY; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/RatKingSprite.java b/src/com/watabou/pixeldungeon/sprites/RatKingSprite.java new file mode 100644 index 0000000000..c0350bff4c --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/RatKingSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class RatKingSprite extends MobSprite { + + public RatKingSprite() { + super(); + + texture( Assets.RATKING ); + + TextureFilm frames = new TextureFilm( texture, 16, 16 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1 ); + + run = new Animation( 10, true ); + run.frames( frames, 2, 3, 4, 5, 6 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 0 ); + + die = new Animation( 10, false ); + die.frames( frames, 0 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/RatSprite.java b/src/com/watabou/pixeldungeon/sprites/RatSprite.java new file mode 100644 index 0000000000..5cf5494fe5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/RatSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class RatSprite extends MobSprite { + + public RatSprite() { + super(); + + texture( Assets.RAT ); + + TextureFilm frames = new TextureFilm( texture, 16, 15 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1 ); + + run = new Animation( 10, true ); + run.frames( frames, 6, 7, 8, 9, 10 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 2, 3, 4, 5, 0 ); + + die = new Animation( 10, false ); + die.frames( frames, 11, 12, 13, 14 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/RottingFistSprite.java b/src/com/watabou/pixeldungeon/sprites/RottingFistSprite.java new file mode 100644 index 0000000000..76872c53bd --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/RottingFistSprite.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class RottingFistSprite extends MobSprite { + + private static final float FALL_SPEED = 64; + + public RottingFistSprite() { + super(); + + texture( Assets.ROTTING ); + + TextureFilm frames = new TextureFilm( texture, 24, 17 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 1 ); + + run = new Animation( 3, true ); + run.frames( frames, 0, 1 ); + + attack = new Animation( 2, false ); + attack.frames( frames, 0 ); + + die = new Animation( 10, false ); + die.frames( frames, 0, 2, 3, 4 ); + + play( idle ); + } + + @Override + public void attack( int cell ) { + super.attack( cell ); + + speed.set( 0, -FALL_SPEED ); + acc.set( 0, FALL_SPEED * 4 ); + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim ); + if (anim == attack) { + speed.set( 0 ); + acc.set( 0 ); + place( ch.pos ); + + Camera.main.shake( 4, 0.2f ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ScorpioSprite.java b/src/com/watabou/pixeldungeon/sprites/ScorpioSprite.java new file mode 100644 index 0000000000..c730ca2f46 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ScorpioSprite.java @@ -0,0 +1,90 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.items.weapon.missiles.Dart; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.utils.Callback; + +public class ScorpioSprite extends MobSprite { + + private int cellToAttack; + + public ScorpioSprite() { + super(); + + texture( Assets.SCORPIO ); + + TextureFilm frames = new TextureFilm( texture, 18, 17 ); + + idle = new Animation( 12, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2 ); + + run = new Animation( 8, true ); + run.frames( frames, 5, 5, 6, 6 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 0, 3, 4 ); + + zap = attack.clone(); + + die = new Animation( 12, false ); + die.frames( frames, 0, 7, 8, 9, 10 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFF44FF22; + } + + @Override + public void attack( int cell ) { + if (!Level.adjacent( cell, ch.pos )) { + + cellToAttack = cell; + turnTo( ch.pos , cell ); + play( zap ); + + } else { + + super.attack( cell ); + + } + } + + @Override + public void onComplete( Animation anim ) { + if (anim == zap) { + idle(); + + ((MissileSprite)parent.recycle( MissileSprite.class )). + reset( ch.pos, cellToAttack, new Dart(), new Callback() { + @Override + public void call() { + ch.onAttackComplete(); + } + } ); + } else { + super.onComplete( anim ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/SeniorSprite.java b/src/com/watabou/pixeldungeon/sprites/SeniorSprite.java new file mode 100644 index 0000000000..c32d2d4e65 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/SeniorSprite.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.Random; + +public class SeniorSprite extends MobSprite { + + private Animation kick; + + public SeniorSprite() { + super(); + + texture( Assets.MONK ); + + TextureFilm frames = new TextureFilm( texture, 15, 14 ); + + idle = new Animation( 6, true ); + idle.frames( frames, 18, 17, 18, 19 ); + + run = new Animation( 15, true ); + run.frames( frames, 28, 29, 30, 31, 32, 33 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 20, 21, 20, 21 ); + + kick = new Animation( 10, false ); + kick.frames( frames, 22, 23, 22 ); + + die = new Animation( 15, false ); + die.frames( frames, 18, 24, 25, 25, 26, 27 ); + + play( idle ); + } + + @Override + public void attack( int cell ) { + super.attack( cell ); + if (Random.Float() < 0.3f) { + play( kick ); + } + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim == kick ? attack : anim ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ShamanSprite.java b/src/com/watabou/pixeldungeon/sprites/ShamanSprite.java new file mode 100644 index 0000000000..8aefaf3a92 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ShamanSprite.java @@ -0,0 +1,62 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.mobs.Shaman; +import com.watabou.pixeldungeon.effects.Lightning; + +public class ShamanSprite extends MobSprite { + + private int[] points = new int[2]; + + public ShamanSprite() { + super(); + + texture( Assets.SHAMAN ); + + TextureFilm frames = new TextureFilm( texture, 12, 15 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1, 0, 0, 1, 1 ); + + run = new Animation( 12, true ); + run.frames( frames, 4, 5, 6, 7 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 2, 3, 0 ); + + zap = attack.clone(); + + die = new Animation( 12, false ); + die.frames( frames, 8, 9, 10 ); + + play( idle ); + } + + public void zap( int pos ) { + + points[0] = ch.pos; + points[1] = pos; + parent.add( new Lightning( points, 2, (Shaman)ch ) ); + + turnTo( ch.pos, pos ); + play( zap ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/SheepSprite.java b/src/com/watabou/pixeldungeon/sprites/SheepSprite.java new file mode 100644 index 0000000000..bd733e9d19 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/SheepSprite.java @@ -0,0 +1,45 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.utils.Random; + +public class SheepSprite extends MobSprite { + + public SheepSprite() { + super(); + + texture( Assets.SHEEP ); + + TextureFilm frames = new TextureFilm( texture, 16, 15 ); + + idle = new Animation( 8, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0 ); + + run = idle.clone(); + attack = idle.clone(); + + die = new Animation( 20, false ); + die.frames( frames, 0 ); + + play( idle ); + curFrame = Random.Int( curAnim.frames.length ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ShieldedSprite.java b/src/com/watabou/pixeldungeon/sprites/ShieldedSprite.java new file mode 100644 index 0000000000..705232b43f --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ShieldedSprite.java @@ -0,0 +1,46 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class ShieldedSprite extends MobSprite { + + public ShieldedSprite() { + super(); + + texture( Assets.BRUTE ); + + TextureFilm frames = new TextureFilm( texture, 12, 16 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 21, 21, 21, 22, 21, 21, 22, 22 ); + + run = new Animation( 12, true ); + run.frames( frames, 25, 26, 27, 28 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 23, 24 ); + + die = new Animation( 12, false ); + die.frames( frames, 29, 30, 31 ); + + play( idle ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ShopkeeperSprite.java b/src/com/watabou/pixeldungeon/sprites/ShopkeeperSprite.java new file mode 100644 index 0000000000..a7094e0695 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ShopkeeperSprite.java @@ -0,0 +1,58 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.pixeldungeon.Assets; + +public class ShopkeeperSprite extends MobSprite { + + private PixelParticle coin; + + public ShopkeeperSprite() { + super(); + + texture( Assets.KEEPER ); + TextureFilm film = new TextureFilm( texture, 14, 14 ); + + idle = new Animation( 10, true ); + idle.frames( film, 1, 1, 1, 1, 1, 0, 0, 0, 0 ); + + run = idle.clone(); + die = idle.clone(); + attack = idle.clone(); + + idle(); + } + + @Override + public void onComplete( Animation anim ) { + super.onComplete( anim ); + + if (visible && anim == idle) { + if (coin == null) { + coin = new PixelParticle(); + parent.add( coin ); + } + coin.reset( x + (flipHorizontal ? 0 : 13), y + 7, 0xFFFF00, 1, 0.5f ); + coin.speed.y = -40; + coin.acc.y = +160; + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/SkeletonSprite.java b/src/com/watabou/pixeldungeon/sprites/SkeletonSprite.java new file mode 100644 index 0000000000..4340d0dc6a --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/SkeletonSprite.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.effects.Speck; + +public class SkeletonSprite extends MobSprite { + + public SkeletonSprite() { + super(); + + texture( Assets.SKELETON ); + + TextureFilm frames = new TextureFilm( texture, 12, 15 ); + + idle = new Animation( 12, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 ); + + run = new Animation( 15, true ); + run.frames( frames, 4, 5, 6, 7, 8, 9 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 14, 15, 16 ); + + die = new Animation( 12, false ); + die.frames( frames, 10, 11, 12, 13 ); + + play( idle ); + } + + @Override + public void die() { + super.die(); + if (Dungeon.visible[ch.pos]) { + emitter().burst( Speck.factory( Speck.BONE ), 6 ); + } + } + + @Override + public int blood() { + return 0xFFcccccc; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/SpinnerSprite.java b/src/com/watabou/pixeldungeon/sprites/SpinnerSprite.java new file mode 100644 index 0000000000..87f73c50d1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/SpinnerSprite.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class SpinnerSprite extends MobSprite { + + public SpinnerSprite() { + super(); + + texture( Assets.SPINNER ); + + TextureFilm frames = new TextureFilm( texture, 16, 16 ); + + idle = new Animation( 10, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 1, 0, 1 ); + + run = new Animation( 15, true ); + run.frames( frames, 0, 2, 0, 3 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 0, 4, 5, 0 ); + + die = new Animation( 12, false ); + die.frames( frames, 6, 7, 8, 9 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFFBFE5B8; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/StatueSprite.java b/src/com/watabou/pixeldungeon/sprites/StatueSprite.java new file mode 100644 index 0000000000..39be3052a1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/StatueSprite.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class StatueSprite extends MobSprite { + + public StatueSprite() { + super(); + + texture( Assets.STATUE ); + + TextureFilm frames = new TextureFilm( texture, 12, 15 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 1, 1 ); + + run = new Animation( 15, true ); + run.frames( frames, 2, 3, 4, 5, 6, 7 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 8, 9, 10 ); + + die = new Animation( 5, false ); + die.frames( frames, 11, 12, 13, 14, 15, 15 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFFcdcdb7; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/SuccubusSprite.java b/src/com/watabou/pixeldungeon/sprites/SuccubusSprite.java new file mode 100644 index 0000000000..95fd8cdbf1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/SuccubusSprite.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.ShadowParticle; + +public class SuccubusSprite extends MobSprite { + + public SuccubusSprite() { + super(); + + texture( Assets.SUCCUBUS ); + + TextureFilm frames = new TextureFilm( texture, 12, 15 ); + + idle = new Animation( 8, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1 ); + + run = new Animation( 15, true ); + run.frames( frames, 3, 4, 5, 6, 7, 8 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 9, 10, 11 ); + + die = new Animation( 10, false ); + die.frames( frames, 12 ); + + play( idle ); + } + + @Override + public void die() { + super.die(); + emitter().burst( Speck.factory( Speck.HEART ), 6 ); + emitter().burst( ShadowParticle.UP, 8 ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/SwarmSprite.java b/src/com/watabou/pixeldungeon/sprites/SwarmSprite.java new file mode 100644 index 0000000000..d9a97ef5fb --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/SwarmSprite.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class SwarmSprite extends MobSprite { + + public SwarmSprite() { + super(); + + texture( Assets.SWARM ); + + TextureFilm frames = new TextureFilm( texture, 16, 16 ); + + idle = new Animation( 15, true ); + idle.frames( frames, 0, 1, 2, 3, 4, 5 ); + + run = new Animation( 15, true ); + run.frames( frames, 0, 1, 2, 3, 4, 5 ); + + attack = new Animation( 20, false ); + attack.frames( frames, 6, 7, 8, 9 ); + + die = new Animation( 15, false ); + die.frames( frames, 10, 11, 12, 13, 14 ); + + play( idle ); + } + + @Override + public int blood() { + return 0xFF8BA077; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/TenguSprite.java b/src/com/watabou/pixeldungeon/sprites/TenguSprite.java new file mode 100644 index 0000000000..16fd336121 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/TenguSprite.java @@ -0,0 +1,99 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.items.weapon.missiles.Shuriken; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.utils.Callback; + +public class TenguSprite extends MobSprite { + + private Animation cast; + + public TenguSprite() { + super(); + + texture( Assets.TENGU ); + + TextureFilm frames = new TextureFilm( texture, 14, 16 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1 ); + + run = new Animation( 15, false ); + run.frames( frames, 2, 3, 4, 5, 0 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 6, 7, 7, 0 ); + + cast = attack.clone(); + + die = new Animation( 8, false ); + die.frames( frames, 8, 9, 10, 10, 10, 10, 10, 10 ); + + play( run.clone() ); + } + + @Override + public void move( int from, int to ) { + + place( to ); + + play( run ); + turnTo( from , to ); + + if (Level.water[to]) { + GameScene.ripple( to ); + } + } + + @Override + public void attack( int cell ) { + if (!Level.adjacent( cell, ch.pos )) { + + ((MissileSprite)parent.recycle( MissileSprite.class )). + reset( ch.pos, cell, new Shuriken(), new Callback() { + @Override + public void call() { + ch.onAttackComplete(); + } + } ); + + play( cast ); + turnTo( ch.pos , cell ); + + } else { + + super.attack( cell ); + + } + } + + @Override + public void onComplete( Animation anim ) { + if (anim == run) { + idle(); + ch.onMotionComplete(); + } else { + super.onComplete( anim ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/ThiefSprite.java b/src/com/watabou/pixeldungeon/sprites/ThiefSprite.java new file mode 100644 index 0000000000..b1cb86fabb --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/ThiefSprite.java @@ -0,0 +1,45 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class ThiefSprite extends MobSprite { + + public ThiefSprite() { + super(); + + texture( Assets.THIEF ); + TextureFilm film = new TextureFilm( texture, 12, 13 ); + + idle = new Animation( 1, true ); + idle.frames( film, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); + + run = new Animation( 15, true ); + run.frames( film, 0, 0, 2, 3, 3, 4 ); + + die = new Animation( 10, false ); + die.frames( film, 5, 6, 7, 8, 9 ); + + attack = new Animation( 12, false ); + attack.frames( film, 10, 11, 12, 0 ); + + idle(); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/UndeadSprite.java b/src/com/watabou/pixeldungeon/sprites/UndeadSprite.java new file mode 100644 index 0000000000..52c5d5d599 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/UndeadSprite.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.effects.Speck; + +public class UndeadSprite extends MobSprite { + + public UndeadSprite() { + super(); + + texture( Assets.UNDEAD ); + + TextureFilm frames = new TextureFilm( texture, 12, 16 ); + + idle = new Animation( 12, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 ); + + run = new Animation( 15, true ); + run.frames( frames, 4, 5, 6, 7, 8, 9 ); + + attack = new Animation( 15, false ); + attack.frames( frames, 14, 15, 16 ); + + die = new Animation( 12, false ); + die.frames( frames, 10, 11, 12, 13 ); + + play( idle ); + } + + @Override + public void die() { + super.die(); + if (Dungeon.visible[ch.pos]) { + emitter().burst( Speck.factory( Speck.BONE ), 3 ); + } + } + + @Override + public int blood() { + return 0xFFcccccc; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/WandmakerSprite.java b/src/com/watabou/pixeldungeon/sprites/WandmakerSprite.java new file mode 100644 index 0000000000..f4f29902b6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/WandmakerSprite.java @@ -0,0 +1,120 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLES20; + +import com.watabou.noosa.Game; +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.effects.Halo; +import com.watabou.pixeldungeon.effects.particles.ElmoParticle; +import com.watabou.utils.PointF; + +public class WandmakerSprite extends MobSprite { + + private Shield shield; + + public WandmakerSprite() { + super(); + + texture( Assets.MAKER ); + + TextureFilm frames = new TextureFilm( texture, 12, 14 ); + + idle = new Animation( 10, true ); + idle.frames( frames, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 2, 1 ); + + run = new Animation( 20, true ); + run.frames( frames, 0 ); + + die = new Animation( 20, false ); + die.frames( frames, 0 ); + + play( idle ); + } + + @Override + public void link( Char ch ) { + super.link( ch ); + + if (shield == null) { + parent.add( shield = new Shield() ); + } + } + + @Override + public void die() { + super.die(); + + if (shield != null) { + shield.putOut(); + } + emitter().start( ElmoParticle.FACTORY, 0.03f, 60 ); + } + + public class Shield extends Halo { + + private float phase; + + public Shield() { + + super( 14, 0xBBAACC, 1f ); + + am = -1; + aa = +1; + + phase = 1; + } + + @Override + public void update() { + super.update(); + + if (phase < 1) { + if ((phase -= Game.elapsed) <= 0) { + killAndErase(); + } else { + scale.set( (2 - phase) * radius / RADIUS ); + am = phase * (-1); + aa = phase * (+1); + } + } + + if (visible = WandmakerSprite.this.visible) { + PointF p = WandmakerSprite.this.center(); + point(p.x, p.y ); + } + } + + @Override + public void draw() { + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE ); + super.draw(); + GLES20.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA ); + } + + public void putOut() { + phase = 0.999f; + } + } + +} diff --git a/src/com/watabou/pixeldungeon/sprites/WarlockSprite.java b/src/com/watabou/pixeldungeon/sprites/WarlockSprite.java new file mode 100644 index 0000000000..21c51ab3a9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/WarlockSprite.java @@ -0,0 +1,75 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.mobs.Warlock; +import com.watabou.pixeldungeon.effects.MagicMissile; +import com.watabou.utils.Callback; + +public class WarlockSprite extends MobSprite { + + public WarlockSprite() { + super(); + + texture( Assets.WARLOCK ); + + TextureFilm frames = new TextureFilm( texture, 12, 15 ); + + idle = new Animation( 2, true ); + idle.frames( frames, 0, 0, 0, 1, 0, 0, 1, 1 ); + + run = new Animation( 15, true ); + run.frames( frames, 0, 2, 3, 4 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 0, 5, 6 ); + + zap = attack.clone(); + + die = new Animation( 15, false ); + die.frames( frames, 0, 7, 8, 8, 9, 10 ); + + play( idle ); + } + + public void zap( int cell ) { + + turnTo( ch.pos , cell ); + play( zap ); + + MagicMissile.shadow( parent, ch.pos, cell, + new Callback() { + @Override + public void call() { + ((Warlock)ch).onZapComplete(); + } + } ); + Sample.INSTANCE.play( Assets.SND_ZAP ); + } + + @Override + public void onComplete( Animation anim ) { + if (anim == zap) { + idle(); + } + super.onComplete( anim ); + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/WraithSprite.java b/src/com/watabou/pixeldungeon/sprites/WraithSprite.java new file mode 100644 index 0000000000..1364948557 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/WraithSprite.java @@ -0,0 +1,51 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; + +public class WraithSprite extends MobSprite { + + public WraithSprite() { + super(); + + texture( Assets.WRAITH ); + + TextureFilm frames = new TextureFilm( texture, 14, 15 ); + + idle = new Animation( 5, true ); + idle.frames( frames, 0, 1 ); + + run = new Animation( 10, true ); + run.frames( frames, 0, 1 ); + + attack = new Animation( 10, false ); + attack.frames( frames, 0, 2, 3 ); + + die = new Animation( 8, false ); + die.frames( frames, 0, 4, 5, 6, 7 ); + + play( idle ); + } + + @Override + public int blood() { + return 0x88000000; + } +} diff --git a/src/com/watabou/pixeldungeon/sprites/YogSprite.java b/src/com/watabou/pixeldungeon/sprites/YogSprite.java new file mode 100644 index 0000000000..848f3240a2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/sprites/YogSprite.java @@ -0,0 +1,54 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.sprites; + +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.effects.Splash; + +public class YogSprite extends MobSprite { + + public YogSprite() { + super(); + + texture( Assets.YOG ); + + TextureFilm frames = new TextureFilm( texture, 20, 19 ); + + idle = new Animation( 10, true ); + idle.frames( frames, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 5, 6, 6, 5 ); + + run = new Animation( 12, true ); + run.frames( frames, 0 ); + + attack = new Animation( 12, false ); + attack.frames( frames, 0 ); + + die = new Animation( 10, false ); + die.frames( frames, 0, 7, 8, 9 ); + + play( idle ); + } + + @Override + public void die() { + super.die(); + + Splash.at( center(), blood(), 12 ); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Archs.java b/src/com/watabou/pixeldungeon/ui/Archs.java new file mode 100644 index 0000000000..64579674fc --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Archs.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Game; +import com.watabou.noosa.SkinnedBlock; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; + +public class Archs extends Component { + + private static final float SCROLL_SPEED = 20f; + + private SkinnedBlock arcsBg; + private SkinnedBlock arcsFg; + + public boolean reversed = false; + + @Override + protected void createChildren() { + arcsBg = new SkinnedBlock( 1, 1, Assets.ARCS_BG ); + add( arcsBg ); + + arcsFg = new SkinnedBlock( 1, 1, Assets.ARCS_FG ); + add( arcsFg ); + } + + @Override + protected void layout() { + arcsBg.size( width, height ); + arcsBg.offset( arcsBg.texture.width / 4 - (width % arcsBg.texture.width) / 2, 0 ); + + arcsFg.size( width, height ); + arcsFg.offset( arcsFg.texture.width / 4 - (width % arcsFg.texture.width) / 2, 0 ); + } + + @Override + public void update() { + + super.update(); + + float shift = Game.elapsed * SCROLL_SPEED; + if (reversed) { + shift = -shift; + } + + arcsBg.offset( 0, shift ); + arcsFg.offset( 0, shift * 2 ); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/AttackIndicator.java b/src/com/watabou/pixeldungeon/ui/AttackIndicator.java new file mode 100644 index 0000000000..7e64ede5f8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/AttackIndicator.java @@ -0,0 +1,167 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import java.util.ArrayList; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.utils.Random; + +public class AttackIndicator extends Tag { + + private static final float ENABLED = 1.0f; + private static final float DISABLED = 0.3f; + + private static AttackIndicator instance; + + private CharSprite sprite = null; + + private static Mob lastTarget = null; + private ArrayList candidates = new ArrayList(); + + public AttackIndicator() { + super( DangerIndicator.COLOR ); + + instance = this; + + setSize( 24, 24 ); + visible( false ); + enable( false ); + } + + @Override + protected void createChildren() { + super.createChildren(); + } + + @Override + protected void layout() { + super.layout(); + + if (sprite != null) { + sprite.x = x + (width - sprite.width()) / 2; + sprite.y = y + (height - sprite.height()) / 2; + PixelScene.align( sprite ); + } + } + + @Override + public void update() { + super.update(); + + if (Dungeon.hero.isAlive()) { + + if (!Dungeon.hero.ready) { + enable( false ); + } + + } else { + visible( false ); + enable( false ); + } + } + + private void checkEnemies() { + + int heroPos = Dungeon.hero.pos; + candidates.clear(); + int v = Dungeon.hero.visibleEnemies(); + for (int i=0; i < v; i++) { + Mob mob = Dungeon.hero.visibleEnemy( i ); + if (Level.adjacent( heroPos, mob.pos )) { + candidates.add( mob ); + } + } + + if (!candidates.contains( lastTarget )) { + if (candidates.isEmpty()) { + lastTarget = null; + } else { + lastTarget = Random.element( candidates ); + updateImage(); + flash(); + } + } else { + if (!bg.visible) { + flash(); + } + } + + visible( lastTarget != null ); + enable( bg.visible ); + } + + private void updateImage() { + + if (sprite != null) { + sprite.killAndErase(); + sprite = null; + } + + try { + sprite = lastTarget.spriteClass.newInstance(); + sprite.idle(); + sprite.paused = true; + add( sprite ); + + sprite.x = x + (width - sprite.width()) / 2 + 1; + sprite.y = y + (height - sprite.height()) / 2; + PixelScene.align( sprite ); + + } catch (Exception e) { + } + } + + private boolean enabled = true; + private void enable( boolean value ) { + enabled = value; + if (sprite != null) { + sprite.alpha( value ? ENABLED : DISABLED ); + } + } + + private void visible( boolean value ) { + bg.visible = value; + if (sprite != null) { + sprite.visible = value; + } + } + + @Override + protected void onClick() { + if (enabled) { + Dungeon.hero.handle( lastTarget.pos ); + } + } + + public static void target( Char target ) { + lastTarget = (Mob)target; + instance.updateImage(); + + HealthIndicator.instance.target( target ); + } + + public static void updateState() { + instance.checkEnemies(); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/BadgesList.java b/src/com/watabou/pixeldungeon/ui/BadgesList.java new file mode 100644 index 0000000000..044f6d04f1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/BadgesList.java @@ -0,0 +1,122 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import java.util.ArrayList; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.effects.BadgeBanner; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.windows.WndBadge; + +public class BadgesList extends ScrollPane { + + private ArrayList items = new ArrayList(); + + public BadgesList( boolean global ) { + super( new Component() ); + + for (Badges.Badge badge : Badges.filtered( global )) { + + if (badge.image == -1) { + continue; + } + + ListItem item = new ListItem( badge ); + content.add( item ); + items.add( item ); + } + } + + @Override + protected void layout() { + super.layout(); + + float pos = 0; + + int size = items.size(); + for (int i=0; i < size; i++) { + items.get( i ).setRect( 0, pos, width, ListItem.HEIGHT ); + pos += ListItem.HEIGHT; + } + + content.setSize( width, pos ); + } + + @Override + public void onClick( float x, float y ) { + int size = items.size(); + for (int i=0; i < size; i++) { + if (items.get( i ).onClick( x, y )) { + break; + } + } + } + + private class ListItem extends Component { + + private static final float HEIGHT = 20; + + private Badges.Badge badge; + + private Image icon; + private BitmapText label; + + public ListItem( Badges.Badge badge ) { + super(); + + this.badge = badge; + icon.copy( BadgeBanner.image( badge.image )); + label.text( badge.description ); + } + + @Override + protected void createChildren() { + icon = new Image(); + add( icon ); + + label = PixelScene.createText( 6 ); + add( label ); + } + + @Override + protected void layout() { + icon.x = x; + icon.y = PixelScene.align( y + (height - icon.height) / 2 ); + + label.x = icon.x + icon.width + 2; + label.y = PixelScene.align( y + (height - label.baseLine()) / 2 ); + } + + public boolean onClick( float x, float y ) { + if (inside( x, y )) { + Sample.INSTANCE.play( Assets.SND_CLICK, 0.7f, 0.7f, 1.2f ); + Game.scene().add( new WndBadge( badge ) ); + return true; + } else { + return false; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Banner.java b/src/com/watabou/pixeldungeon/ui/Banner.java new file mode 100644 index 0000000000..2dcb59dbc1 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Banner.java @@ -0,0 +1,101 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; + +public class Banner extends Image { + + private enum State { + FADE_IN, STATIC, FADE_OUT + }; + private State state; + + private float time; + + private int color; + private float fadeTime; + private float showTime; + + public Banner( Image sample ) { + super(); + copy( sample ); + alpha( 0 ); + } + + public Banner( Object tx ) { + super( tx ); + alpha( 0 ); + } + + public void show( int color, float fadeTime, float showTime ) { + + this.color = color; + this.fadeTime = fadeTime; + this.showTime = showTime; + + state = State.FADE_IN; + + time = fadeTime; + } + + public void show( int color, float fadeTime ) { + show( color, fadeTime, Float.MAX_VALUE ); + } + + @Override + public void update() { + super.update(); + + time -= Game.elapsed; + if (time >= 0) { + + float p = time / fadeTime; + + switch (state) { + case FADE_IN: + tint( color, p ); + alpha( 1 - p ); + break; + case STATIC: + break; + case FADE_OUT: + alpha( p ); + break; + } + + } else { + + switch (state) { + case FADE_IN: + time = showTime; + state = State.STATIC; + break; + case STATIC: + time = fadeTime; + state = State.FADE_OUT; + break; + case FADE_OUT: + killAndErase(); + break; + } + + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/BuffIndicator.java b/src/com/watabou/pixeldungeon/ui/BuffIndicator.java new file mode 100644 index 0000000000..080edc0f23 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/BuffIndicator.java @@ -0,0 +1,143 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.gltextures.SmartTexture; +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.utils.SparseArray; + +public class BuffIndicator extends Component { + + public static final int NONE = -1; + + public static final int MIND_VISION = 0; + public static final int LEVITATION = 1; + public static final int FIRE = 2; + public static final int POISON = 3; + public static final int PARALYSIS = 4; + public static final int HUNGER = 5; + public static final int STARVATION = 6; + public static final int SLOW = 7; + public static final int OOZE = 8; + public static final int AMOK = 9; + public static final int TERROR = 10; + public static final int ROOTS = 11; + public static final int INVISIBLE = 12; + public static final int SHADOWS = 13; + public static final int WEAKNESS = 14; + public static final int FROST = 15; + public static final int BLINDNESS = 16; + public static final int COMBO = 17; + public static final int FURY = 18; + public static final int HEALING = 19; + public static final int ARMOR = 20; + public static final int HEART = 21; + public static final int LIGHT = 22; + public static final int CRIPPLE = 23; + public static final int BARKSKIN = 24; + public static final int IMMUNITY = 25; + public static final int BLEEDING = 26; + public static final int MARK = 27; + public static final int DEFERRED = 28; + + public static final int SIZE = 7; + + private static BuffIndicator heroInstance; + + private SmartTexture texture; + private TextureFilm film; + + private SparseArray icons = new SparseArray(); + + private Char ch; + + public BuffIndicator( Char ch ) { + super(); + + this.ch = ch; + if (ch == Dungeon.hero) { + heroInstance = this; + } + } + + @Override + public void destroy() { + super.destroy(); + + if (this == heroInstance) { + heroInstance = null; + } + } + + @Override + protected void createChildren() { + texture = TextureCache.get( Assets.BUFFS_SMALL ); + film = new TextureFilm( texture, SIZE, SIZE ); + } + + @Override + protected void layout() { + clear(); + + SparseArray newIcons = new SparseArray(); + + for (Buff buff : ch.buffs()) { + int icon = buff.icon(); + if (icon != NONE) { + Image img = new Image( texture ); + img.frame( film.get( icon ) ); + img.x = x + members.size() * (SIZE + 2); + img.y = y; + add( img ); + + newIcons.put( icon, img ); + } + } + + for (Integer key : icons.keyArray()) { + if (newIcons.get( key ) == null) { + Image icon = icons.get( key ); + icon.origin.set( SIZE / 2 ); + add( icon ); + add( new AlphaTweener( icon, 0, 0.6f ) { + @Override + protected void updateValues( float progress ) { + super.updateValues( progress ); + image.scale.set( 1 + 5 * progress ); + }; + } ); + } + } + + icons = newIcons; + } + + public static void refreshHero() { + if (heroInstance != null) { + heroInstance.layout(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/BusyIndicator.java b/src/com/watabou/pixeldungeon/ui/BusyIndicator.java new file mode 100644 index 0000000000..3486aa0f9e --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/BusyIndicator.java @@ -0,0 +1,38 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Dungeon; + +public class BusyIndicator extends Image { + + public BusyIndicator() { + super(); + copy( Icons.BUSY.get() ); + + origin.set( width / 2, height / 2 ); + angularSpeed = 720; + } + + @Override + public void update() { + super.update(); + visible = Dungeon.hero.isAlive() && !Dungeon.hero.ready; + } +} diff --git a/src/com/watabou/pixeldungeon/ui/CheckBox.java b/src/com/watabou/pixeldungeon/ui/CheckBox.java new file mode 100644 index 0000000000..e736891f2e --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/CheckBox.java @@ -0,0 +1,61 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.pixeldungeon.scenes.PixelScene; + +public class CheckBox extends RedButton { + + private boolean checked = false; + + public CheckBox( String label ) { + super( label ); + + icon( Icons.get( Icons.UNCHECKED ) ); + } + + @Override + protected void layout() { + super.layout(); + + float margin = (height - text.baseLine()) / 2; + + text.x = PixelScene.align( PixelScene.uiCamera, x + margin ); + text.y = PixelScene.align( PixelScene.uiCamera, y + margin ); + + icon.x = PixelScene.align( PixelScene.uiCamera, x + width - margin - icon.width ); + icon.y = PixelScene.align( PixelScene.uiCamera, y + (height - icon.height()) / 2 ); + } + + public boolean checked() { + return checked; + } + + public void checked( boolean value ) { + if (checked != value) { + checked = value; + icon.copy( Icons.get( checked ? Icons.CHECKED : Icons.UNCHECKED ) ); + } + } + + @Override + protected void onClick() { + super.onClick(); + checked( !checked ); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Compass.java b/src/com/watabou/pixeldungeon/ui/Compass.java new file mode 100644 index 0000000000..8bba032b5a --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Compass.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.utils.PointF; + +public class Compass extends Image { + + private static final float RAD_2_G = 180f / 3.1415926f; + private static final float RADIUS = 12; + + private int cell; + private PointF cellCenter; + + private PointF lastScroll = new PointF(); + + public Compass( int cell ) { + + super(); + copy( Icons.COMPASS.get() ); + origin.set( width / 2, RADIUS ); + + this.cell = cell; + cellCenter = DungeonTilemap.tileCenterToWorld( cell ); + visible = false; + } + + @Override + public void update() { + super.update(); + + if (!visible) { + visible = Dungeon.level.visited[cell] || Dungeon.level.mapped[cell]; + } + + if (visible) { + PointF scroll = Camera.main.scroll; + if (!scroll.equals( lastScroll )) { + lastScroll.set( scroll ); + PointF center = Camera.main.center().offset( scroll ); + angle = (float)Math.atan2( cellCenter.x - center.x, center.y - cellCenter.y ) * RAD_2_G; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/DangerIndicator.java b/src/com/watabou/pixeldungeon/ui/DangerIndicator.java new file mode 100644 index 0000000000..3c90dfedc0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/DangerIndicator.java @@ -0,0 +1,104 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.scenes.PixelScene; + +public class DangerIndicator extends Tag { + + public static final int COLOR = 0xFF4C4C; + + private BitmapText number; + private Image icon; + + private int enemyIndex = 0; + + private int lastNumber = -1; + + public DangerIndicator() { + super( 0xFF4C4C ); + + setSize( 24, 16 ); + + visible = false; + } + + @Override + protected void createChildren() { + super.createChildren(); + + number = new BitmapText( PixelScene.font1x ); + add( number ); + + icon = Icons.SKULL.get(); + add( icon ); + } + + @Override + protected void layout() { + super.layout(); + + icon.x = right() - 10; + icon.y = y + (height - icon.height) / 2; + + placeNumber(); + } + + private void placeNumber() { + number.x = right() - 11 - number.width(); + number.y = PixelScene.align( y + (height - number.baseLine()) / 2 ); + } + + @Override + public void update() { + + if (Dungeon.hero.isAlive()) { + int v = Dungeon.hero.visibleEnemies(); + if (v != lastNumber) { + lastNumber = v; + if (visible = (lastNumber > 0)) { + number.text( Integer.toString( lastNumber ) ); + number.measure(); + placeNumber(); + + flash(); + } + } + } else { + visible = false; + } + + super.update(); + } + + @Override + protected void onClick() { + + Mob target = Dungeon.hero.visibleEnemy( enemyIndex++ ); + + HealthIndicator.instance.target( target == HealthIndicator.instance.target() ? null : target ); + + Camera.main.target = null; + Camera.main.focusOn( target.sprite ); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/GameLog.java b/src/com/watabou/pixeldungeon/ui/GameLog.java new file mode 100644 index 0000000000..188a84b146 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/GameLog.java @@ -0,0 +1,114 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import java.util.regex.Pattern; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; +import com.watabou.utils.Signal; + +public class GameLog extends Component implements Signal.Listener { + + private static final int MAX_MESSAGES = 3; + + private static final Pattern PUNCTUATION = Pattern.compile( ".*[.,;?! ]$" ); + + private BitmapTextMultiline lastEntry; + private int lastColor; + + public GameLog() { + super(); + GLog.update.add( this ); + + newLine(); + } + + public void newLine() { + lastEntry = null; + } + + @Override + public void onSignal( String text ) { + + int color = CharSprite.DEFAULT; + if (text.startsWith( GLog.POSITIVE )) { + text = text.substring( GLog.POSITIVE.length() ); + color = CharSprite.POSITIVE; + } else + if (text.startsWith( GLog.NEGATIVE )) { + text = text.substring( GLog.NEGATIVE.length() ); + color = CharSprite.NEGATIVE; + } else + if (text.startsWith( GLog.WARNING )) { + text = text.substring( GLog.WARNING.length() ); + color = CharSprite.WARNING; + } else + if (text.startsWith( GLog.HIGHLIGHT )) { + text = text.substring( GLog.HIGHLIGHT.length() ); + color = CharSprite.NEUTRAL; + } + + text = Utils.capitalize( text ) + + (PUNCTUATION.matcher( text ).matches() ? "" : "."); + + if (lastEntry != null && color == lastColor) { + + String lastMessage = lastEntry.text(); + lastEntry.text( lastMessage.length() == 0 ? text : lastMessage + " " + text ); + lastEntry.measure(); + + } else { + + lastEntry = PixelScene.createMultiline( text, 6 ); + lastEntry.maxWidth = (int)width; + lastEntry.measure(); + lastEntry.hardlight( color ); + lastColor = color; + add( lastEntry ); + + } + + if (length > MAX_MESSAGES) { + remove( members.get( 0 ) ); + } + + layout(); + } + + @Override + protected void layout() { + float pos = y; + for (int i=length-1; i >= 0; i--) { + BitmapTextMultiline entry = (BitmapTextMultiline)members.get( i ); + entry.x = x; + entry.y = pos - entry.height(); + pos -= entry.height(); + } + } + + @Override + public void destroy() { + GLog.update.remove( this ); + super.destroy(); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/GoldIndicator.java b/src/com/watabou/pixeldungeon/ui/GoldIndicator.java new file mode 100644 index 0000000000..8586eb48b0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/GoldIndicator.java @@ -0,0 +1,79 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Game; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.scenes.PixelScene; + +public class GoldIndicator extends Component { + + private static final float TIME = 2f; + + private int lastValue = 0; + + private BitmapText tf; + + private float time; + + @Override + protected void createChildren() { + tf = new BitmapText( PixelScene.font1x ); + tf.hardlight( 0xFFFF00 ); + add( tf ); + + visible = false; + } + + @Override + protected void layout() { + tf.x = x + (width - tf.width()) / 2; + tf.y = bottom() - tf.height(); + } + + @Override + public void update() { + super.update(); + + if (visible) { + + time -= Game.elapsed; + if (time > 0) { + tf.alpha( time > TIME / 2 ? 1f : time * 2 / TIME ); + } else { + visible = false; + } + + } + + if (Dungeon.gold != lastValue) { + + lastValue = Dungeon.gold; + + tf.text( Integer.toString( lastValue ) ); + tf.measure(); + + visible = true; + time = TIME; + + layout(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/HealthIndicator.java b/src/com/watabou/pixeldungeon/ui/HealthIndicator.java new file mode 100644 index 0000000000..6415569dce --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/HealthIndicator.java @@ -0,0 +1,82 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.Image; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.sprites.CharSprite; + +public class HealthIndicator extends Component { + + private static final float HEIGHT = 2; + + public static HealthIndicator instance; + + private Char target; + + private Image bg; + private Image level; + + public HealthIndicator() { + super(); + + instance = this; + } + + @Override + protected void createChildren() { + bg = new Image( TextureCache.createSolid( 0xFFcc0000 ) ); + bg.scale.y = HEIGHT; + add( bg ); + + level = new Image( TextureCache.createSolid( 0xFF00cc00 ) ); + level.scale.y = HEIGHT; + add( level ); + } + + @Override + public void update() { + super.update(); + + if (target != null && target.isAlive() && target.sprite.visible) { + CharSprite sprite = target.sprite; + bg.scale.x = sprite.width; + level.scale.x = sprite.width * target.HP / target.HT; + bg.x = level.x = sprite.x; + bg.y = level.y = sprite.y - HEIGHT - 1; + + visible = true; + } else { + visible = false; + } + } + + public void target( Char ch ) { + if (ch != null && ch.isAlive()) { + target = ch; + } else { + target = null; + } + } + + public Char target() { + return target; + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Icons.java b/src/com/watabou/pixeldungeon/ui/Icons.java new file mode 100644 index 0000000000..aedd8d4dbc --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Icons.java @@ -0,0 +1,152 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.actors.hero.HeroClass; + +public enum Icons { + + SKULL, + BUSY, + COMPASS, + INFO, + PREFS, + WARNING, + TARGET, + MASTERY, + WATA, + WARRIOR, + MAGE, + ROGUE, + HUNTRESS, + CLOSE, + DEPTH, + SLEEP, + ALERT, + SUPPORT, + SUPPORTED, + BACKPACK, + SEED_POUCH, + SCROLL_HOLDER, + WAND_HOLSTER, + CHECKED, + UNCHECKED; + + public Image get() { + return get( this ); + } + + public static Image get( Icons type ) { + Image icon = new Image( Assets.ICONS ); + switch (type) { + case SKULL: + icon.frame( icon.texture.uvRect( 0, 0, 8, 8 ) ); + break; + case BUSY: + icon.frame( icon.texture.uvRect( 8, 0, 16, 8 ) ); + break; + case COMPASS: + icon.frame( icon.texture.uvRect( 0, 8, 7, 13 ) ); + break; + case INFO: + icon.frame( icon.texture.uvRect( 16, 0, 30, 14 ) ); + break; + case PREFS: + icon.frame( icon.texture.uvRect( 30, 0, 46, 16 ) ); + break; + case WARNING: + icon.frame( icon.texture.uvRect( 46, 0, 58, 12 ) ); + break; + case TARGET: + icon.frame( icon.texture.uvRect( 0, 13, 16, 29 ) ); + break; + case MASTERY: + icon.frame( icon.texture.uvRect( 16, 14, 30, 28 ) ); + break; + case WATA: + icon.frame( icon.texture.uvRect( 30, 16, 45, 26 ) ); + break; + case WARRIOR: + icon.frame( icon.texture.uvRect( 0, 29, 16, 45 ) ); + break; + case MAGE: + icon.frame( icon.texture.uvRect( 16, 29, 32, 45 ) ); + break; + case ROGUE: + icon.frame( icon.texture.uvRect( 32, 29, 48, 45 ) ); + break; + case HUNTRESS: + icon.frame( icon.texture.uvRect( 48, 29, 64, 45 ) ); + break; + case CLOSE: + icon.frame( icon.texture.uvRect( 0, 45, 13, 58 ) ); + break; + case DEPTH: + icon.frame( icon.texture.uvRect( 45, 12, 54, 20 ) ); + break; + case SLEEP: + icon.frame( icon.texture.uvRect( 13, 45, 22, 53 ) ); + break; + case ALERT: + icon.frame( icon.texture.uvRect( 22, 45, 30, 53 ) ); + break; + case SUPPORT: + icon.frame( icon.texture.uvRect( 30, 45, 46, 61 ) ); + break; + case SUPPORTED: + icon.frame( icon.texture.uvRect( 46, 45, 62, 61 ) ); + break; + case BACKPACK: + icon.frame( icon.texture.uvRect( 58, 0, 68, 10 ) ); + break; + case SCROLL_HOLDER: + icon.frame( icon.texture.uvRect( 68, 0, 78, 10 ) ); + break; + case SEED_POUCH: + icon.frame( icon.texture.uvRect( 78, 0, 88, 10 ) ); + break; + case WAND_HOLSTER: + icon.frame( icon.texture.uvRect( 88, 0, 98, 10 ) ); + break; + case CHECKED: + icon.frame( icon.texture.uvRect( 54, 12, 66, 24 ) ); + break; + case UNCHECKED: + icon.frame( icon.texture.uvRect( 66, 12, 78, 24 ) ); + break; + } + return icon; + } + + public static Image get( HeroClass cl ) { + switch (cl) { + case WARRIOR: + return get( WARRIOR ); + case MAGE: + return get( MAGE ); + case ROGUE: + return get( ROGUE ); + case HUNTRESS: + return get( HUNTRESS ); + default: + return null; + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/ItemSlot.java b/src/com/watabou/pixeldungeon/ui/ItemSlot.java new file mode 100644 index 0000000000..0b762707f5 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/ItemSlot.java @@ -0,0 +1,195 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.weapon.Weapon; +import com.watabou.pixeldungeon.items.weapon.melee.MeleeWeapon; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.utils.Utils; + +public class ItemSlot extends Button { + + public static final int DEGRADED = 0xFF4444; + public static final int UPGRADED = 0x44FF44; + public static final int WARNING = 0xFF8800; + + private static final float ENABLED = 1.0f; + private static final float DISABLED = 0.3f; + + protected ItemSprite icon; + protected BitmapText topLeft; + protected BitmapText topRight; + protected BitmapText bottomRight; + + private static final String TXT_STRENGTH = ":%d"; + private static final String TXT_TYPICAL_STR = "%d?"; + + private static final String TXT_LEVEL = "%+d"; + private static final String TXT_CURSED = ""; + + // Special items for containers + public static final Item CHEST = new Item() { + public int image() { return ItemSpriteSheet.CHEST; }; + }; + public static final Item LOCKED_CHEST = new Item() { + public int image() { return ItemSpriteSheet.LOCKED_CHEST; }; + }; + public static final Item TOMB = new Item() { + public int image() { return ItemSpriteSheet.TOMB; }; + }; + public static final Item SKELETON = new Item() { + public int image() { return ItemSpriteSheet.BONES; }; + }; + + public ItemSlot() { + super(); + } + + public ItemSlot( Item item ) { + this(); + item( item ); + } + + @Override + protected void createChildren() { + + super.createChildren(); + + icon = new ItemSprite(); + add( icon ); + + topLeft = new BitmapText( PixelScene.font1x ); + add( topLeft ); + + topRight = new BitmapText( PixelScene.font1x ); + add( topRight ); + + bottomRight = new BitmapText( PixelScene.font1x ); + add( bottomRight ); + } + + @Override + protected void layout() { + super.layout(); + + icon.x = x + (width - icon.width) / 2; + icon.y = y + (height - icon.height) / 2; + + if (topLeft != null) { + topLeft.x = x; + topLeft.y = y; + } + + if (topRight != null) { + topRight.x = x + (width - topRight.width()); + topRight.y = y; + } + + if (bottomRight != null) { + bottomRight.x = x + (width - bottomRight.width()); + bottomRight.y = y + (height - bottomRight.height()); + } + } + + public void item( Item item ) { + if (item == null) { + + active = false; + icon.visible = topLeft.visible = topRight.visible = bottomRight.visible = false; + + } else { + + active = true; + icon.visible = topLeft.visible = topRight.visible = bottomRight.visible = true; + + icon.view( item.image(), item.glowing() ); + + topLeft.text( item.status() ); + + boolean isArmor = item instanceof Armor; + boolean isWeapon = item instanceof Weapon; + if (isArmor || isWeapon) { + + if (item.levelKnown || (isWeapon && !(item instanceof MeleeWeapon))) { + + int str = isArmor ? ((Armor)item).STR : ((Weapon)item).STR; + topRight.text( Utils.format( TXT_STRENGTH, str ) ); + if (str > Dungeon.hero.STR()) { + topRight.hardlight( DEGRADED ); + } else { + topRight.resetColor(); + } + + } else { + + topRight.text( Utils.format( TXT_TYPICAL_STR, isArmor ? + ((Armor)item).typicalSTR() : + ((MeleeWeapon)item).typicalSTR() ) ); + topRight.hardlight( WARNING ); + + } + topRight.measure(); + + } else { + + topRight.text( null ); + + } + + int level = item.visiblyUpgraded(); + + if (level != 0 || (item.cursed && item.cursedKnown)) { + bottomRight.text( item.levelKnown ? Utils.format( TXT_LEVEL, level ) : TXT_CURSED ); + bottomRight.measure(); + bottomRight.hardlight( level > 0 ? UPGRADED : DEGRADED ); + } else { + bottomRight.text( null ); + } + + layout(); + } + } + + public void enable( boolean value ) { + + active = value; + + float alpha = value ? ENABLED : DISABLED; + icon.alpha( alpha ); + topLeft.alpha( alpha ); + topRight.alpha( alpha ); + bottomRight.alpha( alpha ); + } + + public void showParams( boolean value ) { + if (value) { + add( topRight ); + add( bottomRight ); + } else { + remove( topRight ); + remove( bottomRight ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/LootIndicator.java b/src/com/watabou/pixeldungeon/ui/LootIndicator.java new file mode 100644 index 0000000000..dfaeee349d --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/LootIndicator.java @@ -0,0 +1,93 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; + +public class LootIndicator extends Tag { + + private ItemSlot slot; + + private Item lastItem = null; + private int lastQuantity = 0; + + public LootIndicator() { + super( 0x1F75CC ); + + setSize( 24, 22 ); + + visible = false; + } + + @Override + protected void createChildren() { + super.createChildren(); + + slot = new ItemSlot() { + protected void onClick() { + Dungeon.hero.handle( Dungeon.hero.pos ); + }; + }; + slot.showParams( false ); + add( slot ); + } + + @Override + protected void layout() { + super.layout(); + + slot.setRect( x + 2, y + 3, width - 2, height - 6 ); + } + + @Override + public void update() { + + if (Dungeon.hero.ready) { + Heap heap = Dungeon.level.heaps.get( Dungeon.hero.pos ); + if (heap != null) { + + Item item = + heap.type == Heap.Type.CHEST ? ItemSlot.CHEST : + heap.type == Heap.Type.LOCKED_CHEST ? ItemSlot.LOCKED_CHEST : + heap.type == Heap.Type.TOMB ? ItemSlot.TOMB : + heap.type == Heap.Type.SKELETON ? ItemSlot.SKELETON : + heap.peek(); + if (item != lastItem || item.quantity() != lastQuantity) { + lastItem = item; + lastQuantity = item.quantity(); + + slot.item( item ); + flash(); + } + visible = true; + + } else { + + lastItem = null; + visible = false; + + } + } + + slot.enable( visible && Dungeon.hero.ready ); + + super.update(); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/PrefsButton.java b/src/com/watabou/pixeldungeon/ui/PrefsButton.java new file mode 100644 index 0000000000..f13fbe2697 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/PrefsButton.java @@ -0,0 +1,68 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.windows.WndSettings; + +public class PrefsButton extends Button { + + private Image image; + + public PrefsButton() { + super(); + + width = image.width; + height = image.height; + } + + @Override + protected void createChildren() { + super.createChildren(); + + image = Icons.PREFS.get(); + add( image ); + } + + @Override + protected void layout() { + super.layout(); + + image.x = x; + image.y = y; + } + + @Override + protected void onTouchDown() { + image.brightness( 1.5f ); + Sample.INSTANCE.play( Assets.SND_CLICK ); + } + + @Override + protected void onTouchUp() { + image.resetColor(); + } + + @Override + protected void onClick() { + parent.add( new WndSettings( false ) ); + } +} diff --git a/src/com/watabou/pixeldungeon/ui/QuickSlot.java b/src/com/watabou/pixeldungeon/ui/QuickSlot.java new file mode 100644 index 0000000000..d7a053f194 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/QuickSlot.java @@ -0,0 +1,211 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Image; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.Char; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.windows.WndBag; + +public class QuickSlot extends Button implements WndBag.Listener { + + private static final String TXT_SELECT_ITEM = "Select an item for the quickslot"; + + private static QuickSlot instance; + + private Item itemInSlot; + private ItemSlot slot; + + private Image crossB; + private Image crossM; + + private boolean targeting = false; + private Item lastItem = null; + private Char lastTarget= null; + + public QuickSlot() { + super(); + item( select() ); + + instance = this; + } + + @Override + public void destroy() { + super.destroy(); + + instance = null; + + lastItem = null; + lastTarget = null; + } + + @Override + protected void createChildren() { + super.createChildren(); + + slot = new ItemSlot() { + @Override + protected void onClick() { + if (targeting) { + GameScene.handleCell( lastTarget.pos ); + } else { + Item item = select(); + if (item == lastItem) { + useTargeting(); + } else { + lastItem = item; + } + item.execute( Dungeon.hero ); + } + } + @Override + protected boolean onLongClick() { + return QuickSlot.this.onLongClick(); + } + @Override + protected void onTouchDown() { + icon.lightness( 0.7f ); + } + @Override + protected void onTouchUp() { + icon.resetColor(); + } + }; + add( slot ); + + crossB = Icons.TARGET.get(); + crossB.visible = false; + add( crossB ); + + crossM = new Image(); + crossM.copy( crossB ); + } + + @Override + protected void layout() { + super.layout(); + + slot.fill( this ); + + crossB.x = PixelScene.align( x + (width - crossB.width) / 2 ); + crossB.y = PixelScene.align( y + (height - crossB.height) / 2 ); + } + + @Override + protected void onClick() { + GameScene.selectItem( this, WndBag.Mode.QUICKSLOT, TXT_SELECT_ITEM ); + } + + @Override + protected boolean onLongClick() { + GameScene.selectItem( this, WndBag.Mode.QUICKSLOT, TXT_SELECT_ITEM ); + return true; + } + + @SuppressWarnings("unchecked") + private static Item select() { + if (Dungeon.quickslot instanceof Item) { + + return (Item)Dungeon.quickslot; + + } else if (Dungeon.quickslot != null) { + + Item item = Dungeon.hero.belongings.getItem( (Class)Dungeon.quickslot ); + return item != null ? item : Item.virtual( (Class)Dungeon.quickslot ); + + } else { + + return null; + + } + } + + @Override + public void onSelect( Item item ) { + if (item != null) { + Dungeon.quickslot = item.stackable ? item.getClass() : item; + refresh(); + } + } + + public void item( Item item ) { + slot.item( item ); + itemInSlot = item; + enableSlot(); + } + + public void enable( boolean value ) { + active = value; + if (value) { + enableSlot(); + } else { + slot.enable( false ); + } + } + + private void enableSlot() { + slot.enable( + itemInSlot != null && + itemInSlot.quantity() > 0 && + (Dungeon.hero.belongings.backpack.contains( itemInSlot ) || itemInSlot.isEquipped( Dungeon.hero ))); + } + + private void useTargeting() { + + targeting = lastTarget != null && lastTarget.isAlive() && Dungeon.visible[lastTarget.pos]; + + if (targeting) { + if (Actor.all().contains( lastTarget )) { + lastTarget.sprite.parent.add( crossM ); + crossM.point( DungeonTilemap.tileToWorld( lastTarget.pos ) ); + crossB.visible = true; + } else { + lastTarget = null; + } + } + } + + public static void refresh() { + if (instance != null) { + instance.item( select() ); + } + } + + public static void target( Item item, Char target ) { + if (item == instance.lastItem && target != Dungeon.hero) { + instance.lastTarget = target; + + HealthIndicator.instance.target( target ); + } + } + + public static void cancel() { + if (instance != null && instance.targeting) { + instance.crossB.visible = false; + instance.crossM.remove(); + instance.targeting = false; + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/RedButton.java b/src/com/watabou/pixeldungeon/ui/RedButton.java new file mode 100644 index 0000000000..0305aa23c8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/RedButton.java @@ -0,0 +1,111 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Image; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.scenes.PixelScene; + +public class RedButton extends Button { + + protected NinePatch bg; + protected BitmapText text; + protected Image icon; + + public RedButton( String label ) { + super(); + + text.text( label ); + text.measure(); + } + + @Override + protected void createChildren() { + super.createChildren(); + + bg = Chrome.get( Chrome.Type.BUTTON ); + add( bg ); + + text = PixelScene.createText( 9 ); + add( text ); + } + + @Override + protected void layout() { + + super.layout(); + + bg.x = x; + bg.y = y; + bg.size( width, height ); + + text.x = x + (int)(width - text.width()) / 2; + text.y = y + (int)(height - text.baseLine()) / 2; + + if (icon != null) { + icon.x = x + text.x - icon.width() - 2; + icon.y = y + (height - icon.height()) / 2; + } + }; + + @Override + protected void onTouchDown() { + bg.brightness( 1.2f ); + Sample.INSTANCE.play( Assets.SND_CLICK ); + }; + + @Override + protected void onTouchUp() { + bg.resetColor(); + }; + + public void enable( boolean value ) { + active = value; + text.alpha( value ? 1.0f : 0.3f ); + } + + public void text( String value ) { + text.text( value ); + text.measure(); + layout(); + } + + public void icon( Image icon ) { + if (this.icon != null) { + remove( this.icon ); + } + this.icon = icon; + if (this.icon != null) { + add( this.icon ); + layout(); + } + } + + public float reqWidth() { + return text.width() + 4; + } + + public float reqHeight() { + return text.baseLine() + 4; + } +} diff --git a/src/com/watabou/pixeldungeon/ui/ScrollPane.java b/src/com/watabou/pixeldungeon/ui/ScrollPane.java new file mode 100644 index 0000000000..5600e086b6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/ScrollPane.java @@ -0,0 +1,149 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.Camera; +import com.watabou.noosa.TouchArea; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.utils.Point; +import com.watabou.utils.PointF; + +public class ScrollPane extends Component { + + protected TouchController controller; + protected Component content; + + protected float minX; + protected float minY; + protected float maxX; + protected float maxY; + + public ScrollPane( Component content ) { + super(); + + this.content = content; + addToBack( content ); + + width = content.width(); + height = content.height(); + + content.camera = new Camera( 0, 0, 1, 1, PixelScene.defaultZoom ); + Camera.add( content.camera ); + } + + @Override + public void destroy() { + super.destroy(); + Camera.remove( content.camera ); + } + + public void scrollTo( float x, float y ) { + content.camera.scroll.set( x, y ); + } + + @Override + protected void createChildren() { + controller = new TouchController(); + add( controller ); + } + + @Override + protected void layout() { + + content.setPos( 0, 0 ); + controller.x = x; + controller.y = y; + controller.width = width; + controller.height = height; + + Point p = camera().cameraToScreen( x, y ); + Camera cs = content.camera; + cs.x = p.x; + cs.y = p.y; + cs.resize( (int)width, (int)height ); + } + + public Component content() { + return content; + } + + public void onClick( float x, float y ) { + } + + public class TouchController extends TouchArea { + + private float dragThreshold; + + public TouchController() { + super( 0, 0, 0, 0 ); + dragThreshold = PixelScene.defaultZoom * 8; + } + + @Override + protected void onClick( Touch touch ) { + if (dragging) { + + dragging = false; + + } else { + + PointF p = content.camera.screenToCamera( (int)touch.current.x, (int)touch.current.y ); + ScrollPane.this.onClick( p.x, p.y ); + + } + } + + // true if dragging is in progress + private boolean dragging = false; + // last touch coords + private PointF lastPos = new PointF(); + + @Override + protected void onDrag( Touch t ) { + if (dragging) { + + Camera c = content.camera; + + c.scroll.offset( PointF.diff( lastPos, t.current ).invScale( c.zoom ) ); + if (c.scroll.x + width > content.width()) { + c.scroll.x = content.width() - width; + } + if (c.scroll.x < 0) { + c.scroll.x = 0; + } + if (c.scroll.y + height > content.height()) { + c.scroll.y = content.height() - height; + } + if (c.scroll.y < 0) { + c.scroll.y = 0; + } + + + lastPos.set( t.current ); + + } else if (PointF.distance( t.current, t.start ) > dragThreshold) { + + dragging = true; + lastPos.set( t.current ); + + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/SimpleButton.java b/src/com/watabou/pixeldungeon/ui/SimpleButton.java new file mode 100644 index 0000000000..60ed12f8bc --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/SimpleButton.java @@ -0,0 +1,65 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.Image; +import com.watabou.noosa.TouchArea; +import com.watabou.noosa.ui.Component; + +public class SimpleButton extends Component { + + private Image image; + + public SimpleButton( Image image ) { + super(); + + this.image.copy( image ); + width = image.width; + height = image.height; + } + + @Override + protected void createChildren() { + image = new Image(); + add( image ); + + add( new TouchArea( image ) { + @Override + protected void onTouchDown(Touch touch) { + image.brightness( 1.2f ); + }; + @Override + protected void onTouchUp(Touch touch) { + image.brightness( 1.0f ); + }; + @Override + protected void onClick( Touch touch ) { + SimpleButton.this.onClick(); + }; + } ); + } + + @Override + protected void layout() { + image.x = x; + image.y = y; + } + + protected void onClick() {}; +} diff --git a/src/com/watabou/pixeldungeon/ui/StatusPane.java b/src/com/watabou/pixeldungeon/ui/StatusPane.java new file mode 100644 index 0000000000..f3dd484e34 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/StatusPane.java @@ -0,0 +1,199 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Image; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.TouchArea; +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.effects.Speck; +import com.watabou.pixeldungeon.effects.particles.BloodParticle; +import com.watabou.pixeldungeon.items.keys.IronKey; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.HeroSprite; +import com.watabou.pixeldungeon.windows.WndHero; + +public class StatusPane extends Component { + + private NinePatch shield; + private Image avatar; + private Emitter blood; + + private int lastTier = 0; + + private Image hp; + private Image exp; + + private int lastLvl = -1; + private int lastKeys = -1; + + private BitmapText level; + private BitmapText depth; + private BitmapText keys; + + private DangerIndicator danger; + private LootIndicator loot; + private BuffIndicator buffs; + private Compass compass; + + @Override + protected void createChildren() { + + shield = new NinePatch( Assets.STATUS, 80, 0, 30, 0 ); + add( shield ); + + add( new TouchArea( 0, 1, 30, 30 ) { + @Override + protected void onClick( Touch touch ) { + Image sprite = Dungeon.hero.sprite; + if (!sprite.isVisible()) { + Camera.main.focusOn( sprite ); + } + GameScene.show( new WndHero() ); + }; + } ); + + avatar = HeroSprite.avatar( Dungeon.hero.heroClass, lastTier ); + add( avatar ); + + blood = new Emitter(); + blood.pos( avatar ); + blood.pour( BloodParticle.FACTORY, 0.3f ); + blood.autoKill = false; + blood.on = false; + add( blood ); + + compass = new Compass( Dungeon.level.exit ); + add( compass ); + + hp = new Image( Assets.HP_BAR ); + add( hp ); + + exp = new Image( Assets.XP_BAR ); + add( exp ); + + level = new BitmapText( PixelScene.font1x ); + level.hardlight( 0xFFEBA4 ); + add( level ); + + depth = new BitmapText( Integer.toString( Dungeon.depth ), PixelScene.font1x ); + depth.hardlight( 0xCACFC2 ); + depth.measure(); + add( depth ); + + Dungeon.hero.belongings.countIronKeys(); + keys = new BitmapText( PixelScene.font1x ); + keys.hardlight( 0xCACFC2 ); + add( keys ); + + danger = new DangerIndicator(); + add( danger ); + + loot = new LootIndicator(); + add( loot ); + + buffs = new BuffIndicator( Dungeon.hero ); + add( buffs ); + } + + @Override + protected void layout() { + + height = 32; + + shield.size( width, shield.height ); + + avatar.x = PixelScene.align( camera(), shield.x + 15 - avatar.width / 2 ); + avatar.y = PixelScene.align( camera(), shield.y + 16 - avatar.height / 2 ); + + compass.x = avatar.x + avatar.width / 2 - compass.origin.x; + compass.y = avatar.y + avatar.height / 2 - compass.origin.y; + + hp.x = 30; + hp.y = 3; + + depth.x = width - 24 - depth.width(); + depth.y = 6; + + keys.y = 6; + + danger.setPos( width - danger.width(), 20 ); + + loot.setPos( width - loot.width(), danger.bottom() + 2 ); + + buffs.setPos( 32, 11 ); + } + + @Override + public void update() { + super.update(); + + float health = (float)Dungeon.hero.HP / Dungeon.hero.HT; + + if (health == 0) { + avatar.tint( 0x000000, 0.6f ); + blood.on = false; + } else if (health < 0.25f) { + avatar.tint( 0xcc0000, 0.4f ); + blood.on = true; + } else { + avatar.resetColor(); + blood.on = false; + } + + hp.scale.x = health; + exp.scale.x = (width / exp.width) * Dungeon.hero.exp / Dungeon.hero.maxExp(); + + if (Dungeon.hero.lvl != lastLvl) { + + if (lastLvl != -1) { + Emitter emitter = (Emitter)recycle( Emitter.class ); + emitter.revive(); + emitter.pos( 27, 27 ); + emitter.burst( Speck.factory( Speck.STAR ), 12 ); + } + + lastLvl = Dungeon.hero.lvl; + level.text( Integer.toString( lastLvl ) ); + level.measure(); + level.x = PixelScene.align( 27.0f - level.width() / 2 ); + level.y = PixelScene.align( 27.5f - level.baseLine() / 2 ); + } + + int k = IronKey.curDepthQunatity; + if (k != lastKeys) { + lastKeys = k; + keys.text( Integer.toString( lastKeys ) ); + keys.measure(); + keys.x = width - 8 - keys.width(); + } + + int tier = Dungeon.hero.tier(); + if (tier != lastTier) { + lastTier = tier; + avatar.copy( HeroSprite.avatar( Dungeon.hero.heroClass, tier ) ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Tag.java b/src/com/watabou/pixeldungeon/ui/Tag.java new file mode 100644 index 0000000000..e3d11ce8fc --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Tag.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Game; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Chrome; + +public class Tag extends Button { + + private float r; + private float g; + private float b; + protected NinePatch bg; + + protected float lightness = 0; + + public Tag( int color ) { + super(); + + this.r = (color >> 16) / 255f; + this.g = ((color >> 8) & 0xFF) / 255f; + this.b = (color & 0xFF) / 255f; + } + + @Override + protected void createChildren() { + + super.createChildren(); + + bg = Chrome.get( Chrome.Type.TAG ); + add( bg ); + } + + @Override + protected void layout() { + + super.layout(); + + bg.x = x; + bg.y = y; + bg.size( width, height ); + } + + public void flash() { + lightness = 1f; + } + + @Override + public void update() { + super.update(); + + if (visible && lightness > 0.5) { + if ((lightness -= Game.elapsed) > 0.5) { + bg.ra = bg.ga = bg.ba = 2 * lightness - 1; + bg.rm = 2 * r * (1 - lightness); + bg.gm = 2 * g * (1 - lightness); + bg.bm = 2 * b * (1 - lightness); + } else { + bg.hardlight( r, g, b ); + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Toast.java b/src/com/watabou/pixeldungeon/ui/Toast.java new file mode 100644 index 0000000000..118b732cc8 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Toast.java @@ -0,0 +1,84 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.scenes.PixelScene; + +public class Toast extends Component { + + private static final float MARGIN_HOR = 2; + private static final float MARGIN_VER = 2; + + protected NinePatch bg; + protected SimpleButton close; + protected BitmapText text; + + public Toast( String text ) { + super(); + text( text ); + + width = this.text.width() + close.width() + bg.marginHor() + MARGIN_HOR * 3; + height = Math.max( this.text.height(), close.height() ) + bg.marginVer() + MARGIN_VER * 2; + } + + @Override + protected void createChildren() { + super.createChildren(); + + bg = Chrome.get( Chrome.Type.TOAST_TR ); + add( bg ); + + close = new SimpleButton( Icons.get( Icons.CLOSE ) ) { + protected void onClick() { + onClose(); + }; + }; + add( close ); + + text = PixelScene.createText( 8 ); + add( text ); + } + + @Override + protected void layout() { + super.layout(); + + bg.x = x; + bg.y = y; + bg.size( width, height ); + + close.setPos( + bg.x + bg.width() - bg.marginHor() / 2 - MARGIN_HOR - close.width(), + y + (height - close.height()) / 2 ); + + text.x = close.left() - MARGIN_HOR - text.width(); + text.y = y + (height - text.height()) / 2; + PixelScene.align( text ); + } + + public void text( String txt ) { + text.text( txt ); + text.measure(); + } + + protected void onClose() {}; +} diff --git a/src/com/watabou/pixeldungeon/ui/Toolbar.java b/src/com/watabou/pixeldungeon/ui/Toolbar.java new file mode 100644 index 0000000000..0f317cef7b --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Toolbar.java @@ -0,0 +1,359 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import com.watabou.noosa.Game; +import com.watabou.noosa.Gizmo; +import com.watabou.noosa.Image; +import com.watabou.noosa.ui.Button; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.Actor; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.plants.Plant; +import com.watabou.pixeldungeon.scenes.CellSelector; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.windows.WndCatalogus; +import com.watabou.pixeldungeon.windows.WndHero; +import com.watabou.pixeldungeon.windows.WndInfoCell; +import com.watabou.pixeldungeon.windows.WndInfoItem; +import com.watabou.pixeldungeon.windows.WndInfoMob; +import com.watabou.pixeldungeon.windows.WndInfoPlant; +import com.watabou.pixeldungeon.windows.WndBag; +import com.watabou.pixeldungeon.windows.WndMessage; +import com.watabou.pixeldungeon.windows.WndTradeItem; + +public class Toolbar extends Component { + + private Tool btnWait; + private Tool btnSearch; + private Tool btnInfo; + private Tool btnResume; + private Tool btnInventory; + private Tool btnQuick; + + private PickedUpItem pickedUp; + + private boolean lastEnabled = true; + + public Toolbar() { + super(); + + height = btnInventory.height(); + } + + @Override + protected void createChildren() { + + add( btnWait = new Tool( 0, 7, 20, 24 ) { + @Override + protected void onClick() { + Dungeon.hero.rest( false ); + }; + protected boolean onLongClick() { + Dungeon.hero.rest( true ); + return true; + }; + } ); + + add( btnSearch = new Tool( 20, 7, 20, 24 ) { + @Override + protected void onClick() { + Dungeon.hero.search( true ); + } + } ); + + add( btnInfo = new Tool( 40, 7, 21, 24 ) { + @Override + protected void onClick() { + GameScene.selectCell( informer ); + } + } ); + + add( btnResume = new Tool( 61, 7, 21, 24 ) { + @Override + protected void onClick() { + Dungeon.hero.resume(); + } + } ); + + add( btnInventory = new Tool( 82, 7, 23, 24 ) { + private GoldIndicator gold; + @Override + protected void onClick() { + GameScene.show( new WndBag( Dungeon.hero.belongings.backpack, null, WndBag.Mode.ALL, null ) ); + } + protected boolean onLongClick() { + GameScene.show( new WndCatalogus() ); + return true; + }; + @Override + protected void createChildren() { + super.createChildren(); + gold = new GoldIndicator(); + add( gold ); + }; + @Override + protected void layout() { + super.layout(); + gold.fill( this ); + }; + } ); + + add( btnQuick = new QuickslotTool( 105, 7, 22, 24 ) ); + + add( pickedUp = new PickedUpItem() ); + } + + @Override + protected void layout() { + btnWait.setPos( x, y ); + btnSearch.setPos( btnWait.right(), y ); + btnInfo.setPos( btnSearch.right(), y ); + btnResume.setPos( btnInfo.right(), y ); + btnQuick.setPos( width - btnQuick.width(), y ); + btnInventory.setPos( btnQuick.left() - btnInventory.width(), y ); + } + + @Override + public void update() { + super.update(); + + if (lastEnabled != Dungeon.hero.ready) { + lastEnabled = Dungeon.hero.ready; + + for (Gizmo tool : members) { + if (tool instanceof Tool) { + ((Tool)tool).enable( lastEnabled ); + } + } + } + + btnResume.visible = Dungeon.hero.lastAction != null; + + if (!Dungeon.hero.isAlive()) { + btnInventory.enable( true ); + } + } + + public void pickup( Item item ) { + pickedUp.reset( item, + btnInventory.centerX(), + btnInventory.centerY() ); + } + + private static CellSelector.Listener informer = new CellSelector.Listener() { + @Override + public void onSelect( Integer cell ) { + + if (cell == null) { + return; + } + + if (cell < 0 || cell > Level.LENGTH || (!Dungeon.level.visited[cell] && !Dungeon.level.mapped[cell])) { + GameScene.show( new WndMessage( "You don't know what is there." ) ) ; + return; + } + + if (!Dungeon.visible[cell]) { + GameScene.show( new WndInfoCell( cell ) ); + return; + } + + if (cell == Dungeon.hero.pos) { + GameScene.show( new WndHero() ); + return; + } + + Mob mob = (Mob)Actor.findChar( cell ); + if (mob != null) { + GameScene.show( new WndInfoMob( mob ) ); + return; + } + + Heap heap = Dungeon.level.heaps.get( cell ); + if (heap != null) { + if (heap.type == Heap.Type.FOR_SALE && heap.size() == 1 && heap.peek().price() > 0) { + GameScene.show( new WndTradeItem( heap, false ) ); + } else { + GameScene.show( new WndInfoItem( heap ) ); + } + return; + } + + Plant plant = Dungeon.level.plants.get( cell ); + if (plant != null) { + GameScene.show( new WndInfoPlant( plant ) ); + return; + } + + GameScene.show( new WndInfoCell( cell ) ); + } + @Override + public String prompt() { + return "Select a cell to examine"; + } + }; + + private static class Tool extends Button { + + private static final int BGCOLOR = 0x7B8073; + + private Image base; + + public Tool( int x, int y, int width, int height ) { + super(); + + base.frame( x, y, width, height ); + + this.width = width; + this.height = height; + } + + @Override + protected void createChildren() { + super.createChildren(); + + base = new Image( Assets.TOOLBAR ); + add( base ); + } + + @Override + protected void layout() { + super.layout(); + + base.x = x; + base.y = y; + } + + @Override + protected void onTouchDown() { + base.brightness( 1.4f ); + } + + @Override + protected void onTouchUp() { + if (active) { + base.resetColor(); + } else { + base.tint( BGCOLOR, 0.7f ); + } + } + + public void enable( boolean value ) { + if (value != active) { + if (value) { + base.resetColor(); + } else { + base.tint( BGCOLOR, 0.7f ); + } + active = value; + } + } + } + + private static class QuickslotTool extends Tool { + + private QuickSlot slot; + + public QuickslotTool( int x, int y, int width, int height ) { + super( x, y, width, height ); + } + + @Override + protected void createChildren() { + super.createChildren(); + + slot = new QuickSlot(); + add( slot ); + } + + @Override + protected void layout() { + super.layout(); + slot.setRect( x + 1, y + 2, width - 2, height - 2 ); + } + + @Override + public void enable( boolean value ) { + slot.enable( value ); + active = value; + } + } + + private static class PickedUpItem extends ItemSprite { + + private static final float DISTANCE = DungeonTilemap.SIZE; + private static final float DURATION = 0.2f; + + private float dstX; + private float dstY; + private float left; + + public PickedUpItem() { + super(); + + originToCenter(); + + active = + visible = + false; + } + + public void reset( Item item, float dstX, float dstY ) { + view( item.image(), item.glowing() ); + + active = + visible = + true; + + this.dstX = dstX - ItemSprite.SIZE / 2; + this.dstY = dstY - ItemSprite.SIZE / 2; + left = DURATION; + + x = this.dstX - DISTANCE; + y = this.dstY - DISTANCE; + alpha( 1 ); + } + + @Override + public void update() { + super.update(); + + if ((left -= Game.elapsed) <= 0) { + + visible = + active = + false; + + } else { + float p = left / DURATION; + scale.set( (float)Math.sqrt( p ) ); + float offset = DISTANCE * p; + x = dstX - offset; + y = dstY - offset; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/ui/Window.java b/src/com/watabou/pixeldungeon/ui/Window.java new file mode 100644 index 0000000000..3a1b3c1779 --- /dev/null +++ b/src/com/watabou/pixeldungeon/ui/Window.java @@ -0,0 +1,199 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.ui; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.watabou.input.Keys; +import com.watabou.input.Keys.Key; +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.TouchArea; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.utils.Signal; + +public class Window extends Group implements Signal.Listener { + + protected int width; + protected int height; + + protected TouchArea blocker; + protected NinePatch chrome; + + public static final int TITLE_COLOR = 0xFFFF44; + + public Window() { + this( 0, 0, Chrome.get( Chrome.Type.WINDOW ) ); + } + + public Window( int width, int height ) { + this( width, height, Chrome.get( Chrome.Type.WINDOW ) ); + } + + public Window( int width, int height, NinePatch chrome ) { + super(); + + blocker = new TouchArea( 0, 0, PixelScene.uiCamera.width, PixelScene.uiCamera.height ) { + @Override + protected void onClick( Touch touch ) { + if (!Window.this.chrome.overlapsScreenPoint( + (int)touch.current.x, + (int)touch.current.y )) { + + onBackPressed(); + } + } + }; + blocker.camera = PixelScene.uiCamera; + add( blocker ); + + this.chrome = chrome; + + this.width = width; + this.height = height; + + chrome.x = -chrome.marginLeft(); + chrome.y = -chrome.marginTop(); + chrome.size( + width - chrome.x + chrome.marginRight(), + height - chrome.y + chrome.marginBottom() ); + add( chrome ); + + camera = new Camera( 0, 0, + (int)chrome.width, + (int)chrome.height, + PixelScene.defaultZoom ); + camera.x = (int)(Game.width - camera.width * camera.zoom) / 2; + camera.y = (int)(Game.height - camera.height * camera.zoom) / 2; + camera.scroll.set( chrome.x, chrome.y ); + Camera.add( camera ); + + Keys.event.add( this ); + } + + public void resize( int w, int h ) { + this.width = w; + this.height = h; + + chrome.size( + width + chrome.marginHor(), + height + chrome.marginVer() ); + + camera.resize( (int)chrome.width, (int)chrome.height ); + camera.x = (int)(Game.width - camera.screenWidth()) / 2; + camera.y = (int)(Game.height - camera.screenHeight()) / 2; + } + + public void hide() { + parent.erase( this ); + destroy(); + } + + @Override + public void destroy() { + super.destroy(); + + Camera.remove( camera ); + Keys.event.remove( this ); + } + + @Override + public void onSignal( Key key ) { + if (key.pressed) { + switch (key.code) { + case Keys.BACK: + onBackPressed(); + break; + case Keys.MENU: + onMenuPressed(); + break; + } + } + + Keys.event.cancel(); + } + + public void onBackPressed() { + hide(); + } + + public void onMenuPressed() { + } + + protected static class Highlighter { + + private static final Pattern HIGHLIGHTER = Pattern.compile( "_(.*?)_" ); + private static final Pattern STRIPPER = Pattern.compile( "[ \n]" ); + + public String text; + + public boolean[] mask; + + public Highlighter( String text ) { + + String stripped = STRIPPER.matcher( text ).replaceAll( "" ); + mask = new boolean[stripped.length()]; + + Matcher m = HIGHLIGHTER.matcher( stripped ); + + int pos = 0; + int lastMatch = 0; + + while (m.find()) { + pos += (m.start() - lastMatch); + int groupLen = m.group( 1 ).length(); + for (int i=pos; i < pos + groupLen; i++) { + mask[i] = true; + } + pos += groupLen; + lastMatch = m.end(); + } + + m.reset( text ); + StringBuffer sb = new StringBuffer(); + while (m.find()) { + m.appendReplacement( sb, m.group( 1 ) ); + } + m.appendTail( sb ); + + this.text = sb.toString(); + } + + public boolean[] inverted() { + boolean[] result = new boolean[mask.length]; + for (int i=0; i < result.length; i++) { + result[i] = !mask[i]; + } + return result; + } + + public boolean isHighlighted() { + for (int i=0; i < mask.length; i++) { + if (mask[i]) { + return true; + } + } + return false; + } + } +} diff --git a/src/com/watabou/pixeldungeon/utils/BArray.java b/src/com/watabou/pixeldungeon/utils/BArray.java new file mode 100644 index 0000000000..c109f09fd4 --- /dev/null +++ b/src/com/watabou/pixeldungeon/utils/BArray.java @@ -0,0 +1,140 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.utils; + +public class BArray { + + public static boolean[] and( boolean[] a, boolean[] b, boolean[] result ) { + + int length = a.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = a[i] && b[i]; + } + + return result; + } + + public static boolean[] or( boolean[] a, boolean[] b, boolean[] result ) { + + int length = a.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = a[i] || b[i]; + } + + return result; + } + + public static boolean[] not( boolean[] a, boolean[] result ) { + + int length = a.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = !a[i]; + } + + return result; + } + + public static boolean[] is( int[] a, boolean[] result, int v1 ) { + + int length = a.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = a[i] == v1; + } + + return result; + } + + public static boolean[] isOneOf( int[] a, boolean[] result, int... v ) { + + int length = a.length; + int nv = v.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = false; + for (int j=0; j < nv; j++) { + if (a[i] == v[j]) { + result[i] = true; + break; + } + } + } + + return result; + } + + public static boolean[] isNot( int[] a, boolean[] result, int v1 ) { + + int length = a.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = a[i] != v1; + } + + return result; + } + + public static boolean[] isNotOneOf( int[] a, boolean[] result, int... v ) { + + int length = a.length; + int nv = v.length; + + if (result == null) { + result = new boolean[length]; + } + + for (int i=0; i < length; i++) { + result[i] = true; + for (int j=0; j < nv; j++) { + if (a[i] == v[j]) { + result[i] = false; + break; + } + } + } + + return result; + } +} diff --git a/src/com/watabou/pixeldungeon/utils/GLog.java b/src/com/watabou/pixeldungeon/utils/GLog.java new file mode 100644 index 0000000000..40a0329666 --- /dev/null +++ b/src/com/watabou/pixeldungeon/utils/GLog.java @@ -0,0 +1,60 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.utils; + +import com.watabou.utils.Signal; + +import android.util.Log; + +public class GLog { + + public static final String TAG = "GAME"; + + public static final String POSITIVE = "++ "; + public static final String NEGATIVE = "-- "; + public static final String WARNING = "** "; + public static final String HIGHLIGHT = "@@ "; + + public static Signal update = new Signal(); + + public static void i( String text, Object... args ) { + + if (args.length > 0) { + text = Utils.format( text, args ); + } + + Log.i( TAG, text ); + update.dispatch( text ); + } + + public static void p( String text, Object... args ) { + i( POSITIVE + text, args ); + } + + public static void n( String text, Object... args ) { + i( NEGATIVE + text, args ); + } + + public static void w( String text, Object... args ) { + i( WARNING + text, args ); + } + + public static void h( String text, Object... args ) { + i( HIGHLIGHT + text, args ); + } +} diff --git a/src/com/watabou/pixeldungeon/utils/Utils.java b/src/com/watabou/pixeldungeon/utils/Utils.java new file mode 100644 index 0000000000..4cff2ab6ca --- /dev/null +++ b/src/com/watabou/pixeldungeon/utils/Utils.java @@ -0,0 +1,41 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.utils; + +import java.util.Locale; + +public class Utils { + + public static String capitalize( String str ) { + return Character.toUpperCase( str.charAt( 0 ) ) + str.substring( 1 ); + } + + public static String format( String format, Object...args ) { + return String.format( Locale.ENGLISH, format, args ); + } + + public static String VOWELS = "aoeiu"; + + public static String indefinite( String noun ) { + if (noun.length() == 0) { + return "a"; + } else { + return (VOWELS.indexOf( Character.toLowerCase( noun.charAt( 0 ) ) ) != -1 ? "an " : "a ") + noun; + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/IconTitle.java b/src/com/watabou/pixeldungeon/windows/IconTitle.java new file mode 100644 index 0000000000..92a648a997 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/IconTitle.java @@ -0,0 +1,89 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Image; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class IconTitle extends Component { + + private static final float FONT_SIZE = 9; + + private static final float GAP = 2; + + protected Image imIcon; + protected BitmapTextMultiline tfLabel; + + public IconTitle() { + super(); + } + + public IconTitle( Image icon, String label ) { + super(); + + icon( icon ); + label( label ); + } + + @Override + protected void createChildren() { + imIcon = new Image(); + add( imIcon ); + + tfLabel = PixelScene.createMultiline( FONT_SIZE ); + tfLabel.hardlight( Window.TITLE_COLOR ); + add( tfLabel ); + } + + @Override + protected void layout() { + imIcon.x = 0; + imIcon.y = 0; + + tfLabel.x = PixelScene.align( PixelScene.uiCamera, imIcon.x + imIcon.width() + GAP ); + tfLabel.maxWidth = (int)(width - tfLabel.x); + tfLabel.measure(); + tfLabel.y = PixelScene.align( PixelScene.uiCamera, + imIcon.height > tfLabel.height() ? + (imIcon.height() - tfLabel.baseLine()) / 2 : + imIcon.y ); + + height = Math.max( imIcon.y + imIcon.height(), tfLabel.y + tfLabel.height() ); + } + + public void icon( Image icon ) { + remove( imIcon ); + add( imIcon = icon ); + } + + public void label( String label ) { + tfLabel.text( label ); + } + + public void label( String label, int color ) { + tfLabel.text( label ); + tfLabel.hardlight( color ); + } + + public void color( int color ) { + tfLabel.hardlight( color ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndBadge.java b/src/com/watabou/pixeldungeon/windows/WndBadge.java new file mode 100644 index 0000000000..b0cd1e0893 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndBadge.java @@ -0,0 +1,64 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.effects.BadgeBanner; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class WndBadge extends Window { + + private static final int WIDTH = 120; + private static final int MARGIN = 4; + + public WndBadge( Badges.Badge badge ) { + + super(); + + Image icon = BadgeBanner.image( badge.image ); + icon.scale.set( 2 ); + add( icon ); + + BitmapTextMultiline info = PixelScene.createMultiline( badge.description, 8 ); + info.maxWidth = WIDTH - MARGIN * 2; + info.measure(); + + float w = Math.max( icon.width(), info.width() ) + MARGIN * 2; + + icon.x = (w - icon.width()) / 2; + icon.y = MARGIN; + + float pos = icon.y + icon.height() + MARGIN; + for (BitmapText line : info.new LineSplitter().split()) { + line.measure(); + line.x = PixelScene.align( (w - line.width()) / 2 ); + line.y = PixelScene.align( pos ); + add( line ); + + pos += line.height(); + } + + resize( (int)w, (int)(pos + MARGIN) ); + + BadgeBanner.highlight( icon, badge.image ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndBag.java b/src/com/watabou/pixeldungeon/windows/WndBag.java new file mode 100644 index 0000000000..e74d346b28 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndBag.java @@ -0,0 +1,399 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import android.graphics.RectF; + +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Belongings; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.armor.Armor; +import com.watabou.pixeldungeon.items.bags.Bag; +import com.watabou.pixeldungeon.items.bags.ScrollHolder; +import com.watabou.pixeldungeon.items.bags.SeedPouch; +import com.watabou.pixeldungeon.items.bags.WandHolster; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.items.weapon.melee.MeleeWeapon; +import com.watabou.pixeldungeon.items.weapon.missiles.Boomerang; +import com.watabou.pixeldungeon.plants.Plant.Seed; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.ItemSlot; +import com.watabou.pixeldungeon.ui.QuickSlot; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndBag extends WndTabbed { + + public static enum Mode { + ALL, + UNIDENTIFED, + UPGRADEABLE, + QUICKSLOT, + FOR_SALE, + WEAPON, + ARMOR, + WAND, + SEED + } + + protected static final int COLS = 4; + + protected static final int SLOT_SIZE = 28; + protected static final int SLOT_MARGIN = 1; + + protected static final int TAB_WIDTH = 30; + + protected static final int TITLE_HEIGHT = 12; + + @SuppressWarnings("unused") + protected static final int ROWS = + (Belongings.BACKPACK_SIZE + 4 + 1) / COLS + ((Belongings.BACKPACK_SIZE + 4 + 1) % COLS > 0 ? 1 : 0); + + private Listener listener; + private WndBag.Mode mode; + private String title; + + protected int count; + protected int col; + protected int row; + + private static Mode lastMode; + private static Bag lastBag; + + public WndBag( Bag bag, Listener listener, Mode mode, String title ) { + + super(); + + this.listener = listener; + this.mode = mode; + this.title = title; + + lastMode = mode; + lastBag = bag; + + BitmapText txtTitle = PixelScene.createText( title != null ? title : Utils.capitalize( bag.name() ), 9 ); + txtTitle.hardlight( TITLE_COLOR ); + txtTitle.measure(); + txtTitle.x = (int)(SLOT_SIZE * COLS + SLOT_MARGIN * (COLS - 1) - txtTitle.width()) / 2; + txtTitle.y = (int)(TITLE_HEIGHT - txtTitle.height()) / 2; + add( txtTitle ); + + placeItems( bag ); + + resize( + SLOT_SIZE * COLS + SLOT_MARGIN * (COLS - 1), + SLOT_SIZE * ROWS + SLOT_MARGIN * (ROWS - 1) + TITLE_HEIGHT ); + + Belongings stuff = Dungeon.hero.belongings; + Bag[] bags = { + stuff.backpack, + stuff.getItem( SeedPouch.class ), + stuff.getItem( ScrollHolder.class ), + stuff.getItem( WandHolster.class )}; + + for (Bag b : bags) { + if (b != null) { + BagTab tab = new BagTab( b ); + tab.setSize( TAB_WIDTH, tabHeight() ); + add( tab ); + + tab.select( b == bag ); + } + } + } + + public static WndBag lastBag( Listener listener, Mode mode, String title ) { + + if (mode == lastMode && lastBag != null && + Dungeon.hero.belongings.backpack.contains( lastBag )) { + + return new WndBag( lastBag, listener, mode, title ); + + } else { + + return new WndBag( Dungeon.hero.belongings.backpack, listener, mode, title ); + + } + } + + public static WndBag seedPouch( Listener listener, Mode mode, String title ) { + SeedPouch pouch = Dungeon.hero.belongings.getItem( SeedPouch.class ); + return pouch != null ? + new WndBag( pouch, listener, mode, title ) : + new WndBag( Dungeon.hero.belongings.backpack, listener, mode, title ); + } + + protected void placeItems( Bag container ) { + + // Equipped items + Belongings stuff = Dungeon.hero.belongings; + placeItem( stuff.weapon != null ? stuff.weapon : new Placeholder( ItemSpriteSheet.WEAPON ) ); + placeItem( stuff.armor != null ? stuff.armor : new Placeholder( ItemSpriteSheet.ARMOR ) ); + placeItem( stuff.ring1 != null ? stuff.ring1 : new Placeholder( ItemSpriteSheet.RING ) ); + placeItem( stuff.ring2 != null ? stuff.ring2 : new Placeholder( ItemSpriteSheet.RING ) ); + + // Unequipped items + for (Item item : container.items) { + placeItem( item ); + } + + // Empty slots + while (count-4 < container.size) { + placeItem( null ); + } + + // Gold + if (container == Dungeon.hero.belongings.backpack) { + row = ROWS - 1; + col = COLS - 1; + placeItem( new Gold( Dungeon.gold ) ); + } + } + + protected void placeItem( final Item item ) { + + int x = col * (SLOT_SIZE + SLOT_MARGIN); + int y = TITLE_HEIGHT + row * (SLOT_SIZE + SLOT_MARGIN); + + add( new ItemButton( item ).setPos( x, y ) ); + + if (++col >= COLS) { + col = 0; + row++; + } + + count++; + } + + @Override + public void onMenuPressed() { + if (listener == null) { + hide(); + } + } + + @Override + public void onBackPressed() { + if (listener != null) { + listener.onSelect( null ); + } + super.onBackPressed(); + } + + @Override + protected void onClick( Tab tab ) { + hide(); + GameScene.show( new WndBag( ((BagTab)tab).bag, listener, mode, title ) ); + } + + @Override + protected int tabHeight() { + return 20; + } + + private class BagTab extends Tab { + + private Image icon; + + private Bag bag; + + public BagTab( Bag bag ) { + super(); + + this.bag = bag; + + icon = icon(); + add( icon ); + } + + @Override + protected void select( boolean value ) { + super.select( value ); + icon.am = selected ? 1.0f : 0.6f; + } + + @Override + protected void layout() { + super.layout(); + + icon.copy( icon() ); + icon.x = x + (width - icon.width) / 2; + icon.y = y + (height - icon.height) / 2 - 2 - (selected ? 0 : 1); + if (!selected && icon.y < y + CUT) { + RectF frame = icon.frame(); + frame.top += (y + CUT - icon.y) / icon.texture.height; + icon.frame( frame ); + icon.y = y + CUT; + } + } + + private Image icon() { + if (bag instanceof SeedPouch) { + return Icons.get( Icons.SEED_POUCH ); + } else if (bag instanceof ScrollHolder) { + return Icons.get( Icons.SCROLL_HOLDER ); + } else if (bag instanceof WandHolster) { + return Icons.get( Icons.WAND_HOLSTER ); + } else { + return Icons.get( Icons.BACKPACK ); + } + } + } + + private static class Placeholder extends Item { + { + name = null; + } + + public Placeholder( int image ) { + this.image = image; + } + + @Override + public boolean isIdentified() { + return true; + } + + @Override + public boolean isEquipped( Hero hero ) { + return true; + } + } + + private class ItemButton extends ItemSlot { + + private static final int NORMAL = 0xFF4A4D44; + private static final int EQUIPPED = 0xFF63665B; + + private Item item; + private ColorBlock bg; + + public ItemButton( Item item ) { + + super( item ); + + this.item = item; + if (item instanceof Gold) { + bg.visible = false; + } + + width = height = SLOT_SIZE; + } + + @Override + protected void createChildren() { + bg = new ColorBlock( SLOT_SIZE, SLOT_SIZE, NORMAL ); + add( bg ); + + super.createChildren(); + } + + @Override + protected void layout() { + bg.x = x; + bg.y = y; + + super.layout(); + } + + @Override + public void item( Item item ) { + + super.item( item ); + if (item != null) { + + bg.texture( TextureCache.createSolid( item.isEquipped( Dungeon.hero ) ? EQUIPPED : NORMAL ) ); + if (item.cursed && item.cursedKnown) { + bg.ra = +0.2f; + bg.ga = -0.1f; + } else if (!item.isIdentified()) { + bg.ra = 0.1f; + bg.ba = 0.1f; + } + + if (item.name() == null) { + enable( false ); + } else { + enable( + mode == Mode.FOR_SALE && (item.price() > 0) && (!item.isEquipped( Dungeon.hero ) || !item.cursed) || + mode == Mode.UPGRADEABLE && item.isUpgradable() || + mode == Mode.UNIDENTIFED && !item.isIdentified() || + mode == Mode.QUICKSLOT && (item.defaultAction != null) || + mode == Mode.WEAPON && (item instanceof MeleeWeapon || item instanceof Boomerang) || + mode == Mode.ARMOR && (item instanceof Armor) || + mode == Mode.WAND && (item instanceof Wand) || + mode == Mode.SEED && (item instanceof Seed) || + mode == Mode.ALL + ); + } + } else { + bg.color( NORMAL ); + } + } + + @Override + protected void onTouchDown() { + bg.brightness( 1.5f ); + Sample.INSTANCE.play( Assets.SND_CLICK, 0.7f, 0.7f, 1.2f ); + }; + + protected void onTouchUp() { + bg.brightness( 1.0f ); + }; + + @Override + protected void onClick() { + if (listener != null) { + + hide(); + listener.onSelect( item ); + + } else { + + WndBag.this.add( new WndItem( WndBag.this, item ) ); + + } + } + + @Override + protected boolean onLongClick() { + if (listener == null && item.defaultAction != null) { + hide(); + Dungeon.quickslot = item instanceof Wand ? item : item.getClass(); + QuickSlot.refresh(); + return true; + } else { + return false; + } + } + } + + public interface Listener { + void onSelect( Item item ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndBlacksmith.java b/src/com/watabou/pixeldungeon/windows/WndBlacksmith.java new file mode 100644 index 0000000000..49069ce407 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndBlacksmith.java @@ -0,0 +1,176 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Blacksmith; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.ItemSlot; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndBlacksmith extends Window { + + private static final int BTN_SIZE = 36; + private static final float GAP = 2; + private static final float BTN_GAP = 10; + private static final int WIDTH = 116; + + private ItemButton btnPressed; + + private ItemButton btnItem1; + private ItemButton btnItem2; + private RedButton btnReforge; + + private static final String TXT_PROMPT = + "Ok, a deal is a deal, dat's what I can do for you: I can reforge " + + "2 items and turn them into one of a better quality."; + private static final String TXT_SELECT = + "Select an item to reforge"; + private static final String TXT_REFORGE = + "Reforge them"; + + public WndBlacksmith( Blacksmith troll, Hero hero ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( troll.sprite() ); + titlebar.label( Utils.capitalize( troll.name ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline message = PixelScene.createMultiline( TXT_PROMPT, 6 ); + message.maxWidth = WIDTH; + message.measure(); + message.y = titlebar.bottom() + GAP; + add( message ); + + btnItem1 = new ItemButton() { + @Override + protected void onClick() { + btnPressed = btnItem1; + GameScene.selectItem( itemSelector, WndBag.Mode.UPGRADEABLE, TXT_SELECT ); + } + }; + btnItem1.setRect( (WIDTH - BTN_GAP) / 2 - BTN_SIZE, message.y + message.height() + BTN_GAP, BTN_SIZE, BTN_SIZE ); + add( btnItem1 ); + + btnItem2 = new ItemButton() { + @Override + protected void onClick() { + btnPressed = btnItem2; + GameScene.selectItem( itemSelector, WndBag.Mode.UPGRADEABLE, TXT_SELECT ); + } + }; + btnItem2.setRect( btnItem1.right() + BTN_GAP, btnItem1.top(), BTN_SIZE, BTN_SIZE ); + add( btnItem2 ); + + btnReforge = new RedButton( TXT_REFORGE ) { + @Override + protected void onClick() { + Blacksmith.upgrade( btnItem1.item, btnItem2.item ); + hide(); + } + }; + btnReforge.enable( false ); + btnReforge.setRect( 0, btnItem1.bottom() + BTN_GAP, WIDTH, 20 ); + add( btnReforge ); + + + resize( WIDTH, (int)btnReforge.bottom() ); + } + + protected WndBag.Listener itemSelector = new WndBag.Listener() { + @Override + public void onSelect( Item item ) { + if (item != null) { + btnPressed.item( item ); + + if (btnItem1.item != null && btnItem2.item != null) { + String result = Blacksmith.verify( btnItem1.item, btnItem2.item ); + if (result != null) { + GameScene.show( new WndMessage( result ) ); + btnReforge.enable( false ); + } else { + btnReforge.enable( true ); + } + } + } + } + }; + + public static class ItemButton extends Component { + + protected NinePatch bg; + protected ItemSlot slot; + + public Item item = null; + + @Override + protected void createChildren() { + super.createChildren(); + + bg = Chrome.get( Chrome.Type.BUTTON ); + add( bg ); + + slot = new ItemSlot() { + @Override + protected void onTouchDown() { + bg.brightness( 1.2f ); + Sample.INSTANCE.play( Assets.SND_CLICK ); + }; + @Override + protected void onTouchUp() { + bg.resetColor(); + } + @Override + protected void onClick() { + ItemButton.this.onClick(); + } + }; + add( slot ); + } + + protected void onClick() {}; + + @Override + protected void layout() { + super.layout(); + + bg.x = x; + bg.y = y; + bg.size( width, height ); + + slot.setRect( x + 2, y + 2, width - 4, height - 4 ); + }; + + public void item( Item item ) { + slot.item( this.item = item ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndCatalogus.java b/src/com/watabou/pixeldungeon/windows/WndCatalogus.java new file mode 100644 index 0000000000..04209e9fb9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndCatalogus.java @@ -0,0 +1,189 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import java.util.ArrayList; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.potions.Potion; +import com.watabou.pixeldungeon.items.scrolls.Scroll; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.ScrollPane; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndCatalogus extends WndTabbed { + + private static final int WIDTH = 112; + private static final int HEIGHT = 160; + + private static final int ITEM_HEIGHT = 18; + + private static final int TAB_WIDTH = 50; + + private static final String TXT_POTIONS = "Potions"; + private static final String TXT_SCROLLS = "Scrolls"; + private static final String TXT_TITLE = "Catalogus"; + + private BitmapText txtTitle; + private ScrollPane list; + + private ArrayList items = new ArrayList(); + + private static boolean showPotions = true; + + public WndCatalogus() { + + super(); + resize( WIDTH, HEIGHT ); + + txtTitle = PixelScene.createText( TXT_TITLE, 9 ); + txtTitle.hardlight( Window.TITLE_COLOR ); + txtTitle.measure(); + add( txtTitle ); + + list = new ScrollPane( new Component() ) { + @Override + public void onClick( float x, float y ) { + int size = items.size(); + for (int i=0; i < size; i++) { + if (items.get( i ).onClick( x, y )) { + break; + } + } + } + }; + add( list ); + list.setRect( 0, txtTitle.height(), WIDTH, HEIGHT - txtTitle.height() ); + + boolean showPotions = WndCatalogus.showPotions; + Tab[] tabs = { + new LabeledTab( TXT_POTIONS ) { + protected void select( boolean value ) { + super.select( value ); + WndCatalogus.showPotions = value; + updateList(); + }; + }, + new LabeledTab( TXT_SCROLLS ) { + protected void select( boolean value ) { + super.select( value ); + WndCatalogus.showPotions = !value; + updateList(); + }; + } + }; + for (Tab tab : tabs) { + tab.setSize( TAB_WIDTH, tabHeight() ); + add( tab ); + } + + select( showPotions ? 0 : 1 ); + } + + private void updateList() { + + txtTitle.text( Utils.format( TXT_TITLE, showPotions ? TXT_POTIONS : TXT_SCROLLS ) ); + txtTitle.measure(); + txtTitle.x = PixelScene.align( PixelScene.uiCamera, (WIDTH - txtTitle.width()) / 2 ); + + items.clear(); + + Component content = list.content(); + content.clear(); + list.scrollTo( 0, 0 ); + + float pos = 0; + for (Class itemClass : showPotions ? Potion.getKnown() : Scroll.getKnown()) { + ListItem item = new ListItem( itemClass ); + item.setRect( 0, pos, WIDTH, ITEM_HEIGHT ); + content.add( item ); + items.add( item ); + + pos += item.height(); + } + + for (Class itemClass : showPotions ? Potion.getUnknown() : Scroll.getUnknown()) { + ListItem item = new ListItem( itemClass ); + item.setRect( 0, pos, WIDTH, ITEM_HEIGHT ); + content.add( item ); + items.add( item ); + + pos += item.height(); + } + + content.setSize( WIDTH, pos ); + } + + private static class ListItem extends Component { + + private Item item; + private boolean identified; + + private ItemSprite sprite; + private BitmapText label; + + public ListItem( Class cl ) { + super(); + + try { + item = cl.newInstance(); + if (identified = item.isIdentified()) { + sprite.view( item.image(), null ); + label.text( item.name() ); + } else { + sprite.view( 127, null ); + label.text( item.trueName() ); + label.hardlight( 0xCCCCCC ); + } + } catch (Exception e) { + // + } + } + + @Override + protected void createChildren() { + sprite = new ItemSprite(); + add( sprite ); + + label = PixelScene.createText( 8 ); + add( label ); + } + + @Override + protected void layout() { + sprite.y = PixelScene.align( y + (height - sprite.height) / 2 ); + + label.x = sprite.x + sprite.width; + label.y = PixelScene.align( y + (height - label.baseLine()) / 2 ); + } + + public boolean onClick( float x, float y ) { + if (identified && inside( x, y )) { + GameScene.show( new WndInfoItem( item ) ); + return true; + } else { + return false; + } + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndChooseWay.java b/src/com/watabou/pixeldungeon/windows/WndChooseWay.java new file mode 100644 index 0000000000..c6daf2c02a --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndChooseWay.java @@ -0,0 +1,102 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.actors.hero.HeroSubClass; +import com.watabou.pixeldungeon.items.TomeOfMastery; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndChooseWay extends Window { + + private static final String TXT_MESSAGE = "Which way will you follow?"; + private static final String TXT_CANCEL = "I'll decide later"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + private static final float GAP = 2; + + public WndChooseWay( final TomeOfMastery tome, final HeroSubClass way1, final HeroSubClass way2 ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( tome.image(), null ) ); + titlebar.label( tome.name() ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + Highlighter hl = new Highlighter( way1.desc() + "\n\n" + way2.desc() + "\n\n" + TXT_MESSAGE ); + + BitmapTextMultiline normal = PixelScene.createMultiline( hl.text, 6 ); + normal.maxWidth = WIDTH; + normal.measure(); + normal.x = titlebar.left(); + normal.y = titlebar.bottom() + GAP; + add( normal ); + + if (hl.isHighlighted()) { + normal.mask = hl.inverted(); + + BitmapTextMultiline highlighted = PixelScene.createMultiline( hl.text, 6 ); + highlighted.maxWidth = normal.maxWidth; + highlighted.measure(); + highlighted.x = normal.x; + highlighted.y = normal.y; + add( highlighted ); + + highlighted.mask = hl.mask; + highlighted.hardlight( TITLE_COLOR ); + } + + RedButton btnWay1 = new RedButton( Utils.capitalize( way1.title() ) ) { + @Override + protected void onClick() { + hide(); + tome.choose( way1 ); + } + }; + btnWay1.setRect( 0, normal.y + normal.height() + GAP, (WIDTH - GAP) / 2, BTN_HEIGHT ); + add( btnWay1 ); + + RedButton btnWay2 = new RedButton( Utils.capitalize( way2.title() ) ) { + @Override + protected void onClick() { + hide(); + tome.choose( way2 ); + } + }; + btnWay2.setRect( btnWay1.right() + GAP, btnWay1.top(), btnWay1.width(), BTN_HEIGHT ); + add( btnWay2 ); + + RedButton btnCancel = new RedButton( TXT_CANCEL ) { + @Override + protected void onClick() { + hide(); + } + }; + btnCancel.setRect( 0, btnWay2.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnCancel ); + + resize( WIDTH, (int)btnCancel.bottom() ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndError.java b/src/com/watabou/pixeldungeon/windows/WndError.java new file mode 100644 index 0000000000..099f86e008 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndError.java @@ -0,0 +1,30 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.pixeldungeon.ui.Icons; + +public class WndError extends WndTitledMessage { + + private static final String TXT_TITLE = "ERROR"; + + public WndError( String message ) { + super( Icons.WARNING.get(), TXT_TITLE, message ); + } + +} diff --git a/src/com/watabou/pixeldungeon/windows/WndGame.java b/src/com/watabou/pixeldungeon/windows/WndGame.java new file mode 100644 index 0000000000..4fef81efb2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndGame.java @@ -0,0 +1,120 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import java.io.IOException; + +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.InterlevelScene; +import com.watabou.pixeldungeon.scenes.RankingsScene; +import com.watabou.pixeldungeon.scenes.TitleScene; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; + +public class WndGame extends Window { + + private static final String TXT_SETTINGS = "Settings"; + private static final String TXT_RANKINGS = "Rankings"; + private static final String TXT_START = "Start New Game"; + private static final String TXT_MENU = "Main Menu"; + private static final String TXT_EXIT = "Exit Pixel Dungeon"; + private static final String TXT_RETURN = "Return to Game"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 20; + private static final int GAP = 2; + + private int pos; + + public WndGame() { + + super(); + + addButton( new RedButton( TXT_SETTINGS ) { + @Override + protected void onClick() { + hide(); + GameScene.show( new WndSettings( true ) ); + } + } ); + + // Restart + if (!Dungeon.hero.isAlive()) { + + RedButton btnStart; + addButton( btnStart = new RedButton( TXT_START ) { + @Override + protected void onClick() { + Dungeon.hero = null; + InterlevelScene.mode = InterlevelScene.Mode.DESCEND; + InterlevelScene.noStory = true; + Game.switchScene( InterlevelScene.class ); + } + } ); + btnStart.icon( Icons.get( Dungeon.hero.heroClass ) ); + + addButton( new RedButton( TXT_RANKINGS ) { + @Override + protected void onClick() { + InterlevelScene.mode = InterlevelScene.Mode.DESCEND; + Game.switchScene( RankingsScene.class ); + } + } ); + } + + // Main menu + addButton( new RedButton( TXT_MENU ) { + @Override + protected void onClick() { + try { + Dungeon.saveAll(); + } catch (IOException e) { + // + } + Game.switchScene( TitleScene.class ); + } + } ); + + // Exit + addButton( new RedButton( TXT_EXIT ) { + @Override + protected void onClick() { + Game.instance.finish(); + } + } ); + + // Cancel + addButton( new RedButton( TXT_RETURN ) { + @Override + protected void onClick() { + hide(); + } + } ); + + resize( WIDTH, pos ); + } + + private void addButton( RedButton btn ) { + add( btn ); + btn.setRect( 0, pos > 0 ? pos += GAP : 0, WIDTH, BTN_HEIGHT ); + pos += BTN_HEIGHT; + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndHero.java b/src/com/watabou/pixeldungeon/windows/WndHero.java new file mode 100644 index 0000000000..46cf4a18ff --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndHero.java @@ -0,0 +1,209 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import java.util.Locale; + +import com.watabou.gltextures.SmartTexture; +import com.watabou.gltextures.TextureCache; +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.noosa.TextureFilm; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.buffs.Buff; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.scenes.GameScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndHero extends WndTabbed { + + private static final String TXT_STATS = "Stats"; + private static final String TXT_BUFFS = "Buffs"; + + private static final String TXT_EXP = "Experience"; + private static final String TXT_STR = "Strength"; + private static final String TXT_HEALTH = "Health"; + private static final String TXT_GOLD = "Gold Collected"; + private static final String TXT_DEPTH = "Maximum Depth"; + + private static final int WIDTH = 100; + private static final int TAB_WIDTH = 40; + + private StatsTab stats; + private BuffsTab buffs; + + private SmartTexture icons; + private TextureFilm film; + + public WndHero() { + + super(); + + icons = TextureCache.get( Assets.BUFFS_LARGE ); + film = new TextureFilm( icons, 16, 16 ); + + stats = new StatsTab(); + add( stats ); + + buffs = new BuffsTab(); + add( buffs ); + + add( new LabeledTab( TXT_STATS ) { + protected void select( boolean value ) { + super.select( value ); + stats.visible = stats.active = selected; + }; + } ); + add( new LabeledTab( TXT_BUFFS ) { + protected void select( boolean value ) { + super.select( value ); + buffs.visible = buffs.active = selected; + }; + } ); + for (Tab tab : tabs) { + tab.setSize( TAB_WIDTH, tabHeight() ); + } + + resize( WIDTH, (int)Math.max( stats.height(), buffs.height() ) ); + + select( 0 ); + } + + private class StatsTab extends Group { + + private static final String TXT_TITLE = "Level %d %s"; + private static final String TXT_CATALOGUS = "Catalogus"; + private static final String TXT_JOURNAL = "Journal"; + + private static final int GAP = 5; + + private float pos; + + public StatsTab() { + + Hero hero = Dungeon.hero; + + BitmapText title = PixelScene.createText( + Utils.format( TXT_TITLE, hero.lvl, hero.className() ).toUpperCase( Locale.ENGLISH ), 9 ); + title.hardlight( TITLE_COLOR ); + title.measure(); + add( title ); + + RedButton btnCatalogus = new RedButton( TXT_CATALOGUS ) { + @Override + protected void onClick() { + hide(); + GameScene.show( new WndCatalogus() ); + } + }; + btnCatalogus.setRect( 0, title.y + title.height(), btnCatalogus.reqWidth() + 2, btnCatalogus.reqHeight() + 2 ); + add( btnCatalogus ); + + RedButton btnJournal = new RedButton( TXT_JOURNAL ) { + @Override + protected void onClick() { + hide(); + GameScene.show( new WndJournal() ); + } + }; + btnJournal.setRect( + btnCatalogus.right() + 1, btnCatalogus.top(), + btnJournal.reqWidth() + 2, btnJournal.reqHeight() + 2 ); + add( btnJournal ); + + pos = btnCatalogus.bottom() + GAP; + + statSlot( TXT_STR, hero.STR() ); + statSlot( TXT_HEALTH, hero.HP + "/" + hero.HT ); + statSlot( TXT_EXP, hero.exp + "/" + hero.maxExp() ); + + pos += GAP; + + statSlot( TXT_GOLD, Statistics.goldCollected ); + statSlot( TXT_DEPTH, Statistics.deepestFloor ); + + pos += GAP; + } + + private void statSlot( String label, String value ) { + + BitmapText txt = PixelScene.createText( label, 8 ); + txt.y = pos; + add( txt ); + + txt = PixelScene.createText( value, 8 ); + txt.measure(); + txt.x = PixelScene.align( WIDTH * 0.65f ); + txt.y = pos; + add( txt ); + + pos += GAP + txt.baseLine(); + } + + private void statSlot( String label, int value ) { + statSlot( label, Integer.toString( value ) ); + } + + public float height() { + return pos; + } + } + + private class BuffsTab extends Group { + + private static final int GAP = 2; + + private float pos; + + public BuffsTab() { + for (Buff buff : Dungeon.hero.buffs()) { + buffSlot( buff ); + } + } + + private void buffSlot( Buff buff ) { + + int index = buff.icon(); + + if (index != BuffIndicator.NONE) { + + Image icon = new Image( icons ); + icon.frame( film.get( index ) ); + icon.y = pos; + add( icon ); + + BitmapText txt = PixelScene.createText( buff.toString(), 8 ); + txt.x = icon.width + GAP; + txt.y = pos + (int)(icon.height - txt.baseLine()) / 2; + add( txt ); + + pos += GAP + icon.height; + } + } + + public float height() { + return pos; + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndImp.java b/src/com/watabou/pixeldungeon/windows/WndImp.java new file mode 100644 index 0000000000..8f51447488 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndImp.java @@ -0,0 +1,90 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Imp; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.quest.DwarfToken; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndImp extends Window { + + private static final String TXT_MESSAGE = + "Oh yes! You are my hero!\n" + + "Regarding your reward, I don't have cash with me right now, but I have something better for you. " + + "This is my family heirloom ring: my granddad took it off a dead paladin's finger."; + private static final String TXT_REWARD = "Take the ring"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + private static final float GAP = 2; + + public WndImp( final Imp imp, final DwarfToken tokens ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( tokens.image(), null ) ); + titlebar.label( Utils.capitalize( tokens.name() ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline message = PixelScene.createMultiline( TXT_MESSAGE, 6 ); + message.maxWidth = WIDTH; + message.measure(); + message.y = titlebar.bottom() + GAP; + add( message ); + + RedButton btnReward = new RedButton( TXT_REWARD ) { + @Override + protected void onClick() { + takeReward( imp, tokens, Imp.Quest.reward ); + } + }; + btnReward.setRect( 0, message.y + message.height() + GAP, WIDTH, BTN_HEIGHT ); + add( btnReward ); + + resize( WIDTH, (int)btnReward.bottom() ); + } + + private void takeReward( Imp imp, DwarfToken tokens, Item reward ) { + + hide(); + + tokens.detachAll( Dungeon.hero.belongings.backpack ); + + reward.identify(); + if (reward.doPickUp( Dungeon.hero )) { + GLog.i( Hero.TXT_YOU_NOW_HAVE, reward.name() ); + } else { + Dungeon.level.drop( reward, imp.pos ).sprite.drop(); + } + + imp.flee(); + + Imp.Quest.complete(); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndInfoCell.java b/src/com/watabou/pixeldungeon/windows/WndInfoCell.java new file mode 100644 index 0000000000..4220c0f8c2 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndInfoCell.java @@ -0,0 +1,84 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Image; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.DungeonTilemap; +import com.watabou.pixeldungeon.actors.blobs.Blob; +import com.watabou.pixeldungeon.levels.Level; +import com.watabou.pixeldungeon.levels.Terrain; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class WndInfoCell extends Window { + + private static final float GAP = 2; + + private static final int WIDTH = 120; + + private static final String TXT_NOTHING = "There is nothing here."; + + public WndInfoCell( int cell ) { + + super(); + + int tile = Dungeon.level.map[cell]; + if (Level.water[cell]) { + tile = Terrain.WATER; + } else if (Level.pit[cell]) { + tile = Terrain.CHASM; + } + + IconTitle titlebar = new IconTitle(); + if (tile == Terrain.WATER) { + Image water = new Image( Dungeon.level.waterTex() ); + water.frame( 0, 0, DungeonTilemap.SIZE, DungeonTilemap.SIZE ); + titlebar.icon( water ); + } else { + titlebar.icon( DungeonTilemap.tile( tile ) ); + } + titlebar.label( Dungeon.level.tileName( tile ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline info = PixelScene.createMultiline( 6 ); + add( info ); + + StringBuilder desc = new StringBuilder( Dungeon.level.tileDesc( tile ) ); + + final char newLine = '\n'; + for (Blob blob:Dungeon.level.blobs.values()) { + if (blob.cur[cell] > 0 && blob.tileDesc() != null) { + if (desc.length() > 0) { + desc.append( newLine ); + } + desc.append( blob.tileDesc() ); + } + } + + info.text( desc.length() > 0 ? desc.toString() : TXT_NOTHING ); + info.maxWidth = WIDTH; + info.measure(); + info.x = titlebar.left(); + info.y = titlebar.bottom() + GAP; + + resize( WIDTH, (int)(info.y + info.height()) ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndInfoItem.java b/src/com/watabou/pixeldungeon/windows/WndInfoItem.java new file mode 100644 index 0000000000..8188d330cc --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndInfoItem.java @@ -0,0 +1,125 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Heap.Type; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.ItemSlot; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndInfoItem extends Window { + + private static final String TXT_CHEST = "Chest"; + private static final String TXT_LOCKED_CHEST = "Locked chest"; + private static final String TXT_CRYSTAL_CHEST = "Crystal chest"; + private static final String TXT_TOMB = "Tomb"; + private static final String TXT_SKELETON = "Skeletal remains"; + private static final String TXT_WONT_KNOW = "You won't know what's inside until you open it!"; + private static final String TXT_NEED_KEY = TXT_WONT_KNOW + " But to open it you need a golden key."; + private static final String TXT_INSIDE = "You can see %s inside, but to open the chest you need a golden key."; + private static final String TXT_OWNER = + "This ancient tomb may contain something useful, " + + "but its owner will most certainly object to checking."; + private static final String TXT_REMAINS = + "This is all that's left from one of your predecessors. " + + "Maybe it's worth checking for any valuables."; + + private static final float GAP = 2; + + private static final int WIDTH = 120; + + public WndInfoItem( Heap heap ) { + + super(); + + if (heap.type == Heap.Type.HEAP || heap.type == Heap.Type.FOR_SALE) { + + Item item = heap.peek(); + + int color = TITLE_COLOR; + if (item.levelKnown && item.level > 0) { + color = ItemSlot.UPGRADED; + } else if (item.levelKnown && item.level < 0) { + color = ItemSlot.DEGRADED; + } + fillFields( item.image(), item.glowing(), color, item.toString(), item.info() ); + + } else { + + String title; + String info; + + if (heap.type == Type.CHEST) { + title = TXT_CHEST; + info = TXT_WONT_KNOW; + } else if (heap.type == Type.TOMB) { + title = TXT_TOMB; + info = TXT_OWNER; + } else if (heap.type == Type.SKELETON) { + title = TXT_SKELETON; + info = TXT_REMAINS; + } else if (heap.type == Type.CRYSTAL_CHEST) { + title = TXT_CRYSTAL_CHEST; + info = Utils.format( TXT_INSIDE, Utils.indefinite( heap.peek().name() ) ); + } else { + title = TXT_LOCKED_CHEST; + info = TXT_NEED_KEY; + } + + fillFields( heap.image(), heap.glowing(), TITLE_COLOR, title, info ); + + } + } + + public WndInfoItem( Item item ) { + + super(); + + int color = TITLE_COLOR; + if (item.levelKnown && item.level > 0) { + color = ItemSlot.UPGRADED; + } else if (item.levelKnown && item.level < 0) { + color = ItemSlot.DEGRADED; + } + + fillFields( item.image(), item.glowing(), color, item.toString(), item.info() ); + } + + private void fillFields( int image, ItemSprite.Glowing glowing, int titleColor, String title, String info ) { + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( image, glowing ) ); + titlebar.label( Utils.capitalize( title ), titleColor ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline txtInfo = PixelScene.createMultiline( info, 6 ); + txtInfo.maxWidth = WIDTH; + txtInfo.measure(); + txtInfo.x = titlebar.left(); + txtInfo.y = titlebar.bottom() + GAP; + add( txtInfo ); + + resize( WIDTH, (int)(txtInfo.y + txtInfo.height()) ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndInfoMob.java b/src/com/watabou/pixeldungeon/windows/WndInfoMob.java new file mode 100644 index 0000000000..bb7743555d --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndInfoMob.java @@ -0,0 +1,130 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.CharSprite; +import com.watabou.pixeldungeon.ui.BuffIndicator; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndInfoMob extends WndTitledMessage { + + private static final String TXT_SLEEPNIG = "\n\nThis %s is sleeping."; + private static final String TXT_HUNTING = "\n\nThis %s is hunting."; + private static final String TXT_WANDERING = "\n\nThis %s is wandering."; + private static final String TXT_FLEEING = "\n\nThis %s is fleeing."; + private static final String TXT_PASSIVE = "\n\nThe %s is passive."; + + public WndInfoMob( Mob mob ) { + + super( new MobTitle( mob ), desc( mob ) ); + + } + + private static String desc( Mob mob ) { + + StringBuilder builder = new StringBuilder( mob.description() ); + + switch (mob.state) { + case SLEEPING: + builder.append( String.format( TXT_SLEEPNIG, mob.name ) ); + break; + case HUNTING: + builder.append( String.format( TXT_HUNTING, mob.name ) ); + break; + case WANDERING: + builder.append( String.format( TXT_WANDERING, mob.name ) ); + break; + case FLEEING: + builder.append( String.format( TXT_FLEEING, mob.name ) ); + break; + case PASSIVE: + builder.append( String.format( TXT_PASSIVE, mob.name ) ); + break; + } + + return builder.toString(); + } + + private static class MobTitle extends Component { + + private static final int COLOR_BG = 0xFFCC0000; + private static final int COLOR_LVL = 0xFF00EE00; + + private static final int BAR_HEIGHT = 2; + private static final int GAP = 2; + + private CharSprite image; + private BitmapText name; + private ColorBlock hpBg; + private ColorBlock hpLvl; + private BuffIndicator buffs; + + private float hp; + + public MobTitle( Mob mob ) { + + hp = (float)mob.HP / mob.HT; + + name = PixelScene.createText( Utils.capitalize( mob.name ), 9 ); + name.hardlight( TITLE_COLOR ); + name.measure(); + add( name ); + + image = mob.sprite(); + add( image ); + + hpBg = new ColorBlock( 1, 1, COLOR_BG ); + add( hpBg ); + + hpLvl = new ColorBlock( 1, 1, COLOR_LVL ); + add( hpLvl ); + + buffs = new BuffIndicator( mob ); + add( buffs ); + } + + @Override + protected void layout() { + + image.x = 0; + image.y = Math.max( 0, name.height() + GAP + BAR_HEIGHT - image.height ); + + name.x = image.width + GAP; + name.y = image.height - BAR_HEIGHT - GAP - name.baseLine(); + + float w = width - image.width - GAP; + + hpBg.size( w, BAR_HEIGHT ); + hpLvl.size( w * hp, BAR_HEIGHT ); + + hpBg.x = hpLvl.x = image.width + GAP; + hpBg.y = hpLvl.y = image.height - BAR_HEIGHT; + + buffs.setPos( + name.x + name.width() + GAP, + name.y + name.baseLine() - BuffIndicator.SIZE ); + + height = hpBg.y + hpBg.height(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndInfoPlant.java b/src/com/watabou/pixeldungeon/windows/WndInfoPlant.java new file mode 100644 index 0000000000..f74982895a --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndInfoPlant.java @@ -0,0 +1,53 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.plants.Plant; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.PlantSprite; +import com.watabou.pixeldungeon.ui.Window; + +public class WndInfoPlant extends Window { + + private static final float GAP = 2; + + private static final int WIDTH = 120; + + public WndInfoPlant( Plant plant ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new PlantSprite( plant.image ) ); + titlebar.label( plant.plantName ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline info = PixelScene.createMultiline( 6 ); + add( info ); + + info.text( plant.desc() ); + info.maxWidth = WIDTH; + info.measure(); + info.x = titlebar.left(); + info.y = titlebar.bottom() + GAP; + + resize( WIDTH, (int)(info.y + info.height()) ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndItem.java b/src/com/watabou/pixeldungeon/windows/WndItem.java new file mode 100644 index 0000000000..66326a2723 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndItem.java @@ -0,0 +1,90 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.ItemSlot; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndItem extends Window { + + private static final float BUTTON_WIDTH = 36; + private static final float BUTTON_HEIGHT = 16; + + private static final float GAP = 2; + + private static final int WIDTH = 120; + + public WndItem( final WndBag owner, final Item item ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( item.image(), item.glowing() ) ); + titlebar.label( Utils.capitalize( item.toString() ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + if (item.levelKnown && item.level > 0) { + titlebar.color( ItemSlot.UPGRADED ); + } else if (item.levelKnown && item.level < 0) { + titlebar.color( ItemSlot.DEGRADED ); + } + + BitmapTextMultiline info = PixelScene.createMultiline( item.info(), 6 ); + info.maxWidth = WIDTH; + info.measure(); + info.x = titlebar.left(); + info.y = titlebar.bottom() + GAP; + add( info ); + + float y = info.y + info.height() + GAP; + float x = 0; + + if (Dungeon.hero.isAlive() && owner != null) { + for (final String action:item.actions( Dungeon.hero )) { + + RedButton btn = new RedButton( action ) { + @Override + protected void onClick() { + item.execute( Dungeon.hero, action ); + hide(); + owner.hide(); + }; + }; + btn.setSize( Math.max( BUTTON_WIDTH, btn.reqWidth() ), BUTTON_HEIGHT ); + if (x + btn.width() > WIDTH) { + x = 0; + y += BUTTON_HEIGHT + GAP; + } + btn.setPos( x, y ); + add( btn ); + + x += btn.width() + GAP; + } + } + + resize( WIDTH, (int)(y + (x > 0 ? BUTTON_HEIGHT : 0)) ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndJournal.java b/src/com/watabou/pixeldungeon/windows/WndJournal.java new file mode 100644 index 0000000000..1b84e8f894 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndJournal.java @@ -0,0 +1,123 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import java.util.Collections; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Image; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Journal; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.ScrollPane; +import com.watabou.pixeldungeon.ui.Window; + +public class WndJournal extends Window { + + private static final int WIDTH = 112; + private static final int HEIGHT = 160; + + private static final int ITEM_HEIGHT = 18; + + private static final String TXT_TITLE = "Journal"; + + private BitmapText txtTitle; + private ScrollPane list; + + public WndJournal() { + + super(); + resize( WIDTH, HEIGHT ); + + txtTitle = PixelScene.createText( TXT_TITLE, 9 ); + txtTitle.hardlight( Window.TITLE_COLOR ); + txtTitle.measure(); + txtTitle.x = PixelScene.align( PixelScene.uiCamera, (WIDTH - txtTitle.width()) / 2 ); + add( txtTitle ); + + Component content = new Component(); + + Collections.sort( Journal.records ); + + float pos = 0; + for (Journal.Record rec : Journal.records) { + ListItem item = new ListItem( rec.feature, rec.depth ); + item.setRect( 0, pos, WIDTH, ITEM_HEIGHT ); + content.add( item ); + + pos += item.height(); + } + + content.setSize( WIDTH, pos ); + + list = new ScrollPane( content ); + add( list ); + + list.setRect( 0, txtTitle.height(), WIDTH, HEIGHT - txtTitle.height() ); + } + + private static class ListItem extends Component { + + private BitmapText feature; + private BitmapText depth; + + private Image icon; + + public ListItem( Journal.Feature f, int d ) { + super(); + + feature.text( f.desc ); + feature.measure(); + + depth.text( Integer.toString( d ) ); + depth.measure(); + + if (d == Dungeon.depth) { + feature.hardlight( TITLE_COLOR ); + depth.hardlight( TITLE_COLOR ); + } + } + + @Override + protected void createChildren() { + feature = PixelScene.createText( 9 ); + add( feature ); + + depth = new BitmapText( PixelScene.font1x ); + add( depth ); + + icon = Icons.get( Icons.DEPTH ); + add( icon ); + } + + @Override + protected void layout() { + + icon.x = width - icon.width; + + depth.x = icon.x - 1 - depth.width(); + depth.y = PixelScene.align( y + (height - depth.height()) / 2 ); + + icon.y = depth.y - 1; + + feature.y = PixelScene.align( depth.y + depth.baseLine() - feature.baseLine() ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndList.java b/src/com/watabou/pixeldungeon/windows/WndList.java new file mode 100644 index 0000000000..72b33749a9 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndList.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class WndList extends Window { + + private static final int WIDTH = 120; + private static final int MARGIN = 4; + private static final int GAP = 4; + + private static final String DOT = "\u007F"; + + public WndList( String[] items ) { + + super(); + + float pos = MARGIN; + float dotWidth = 0; + float maxWidth = 0; + + for (int i=0; i < items.length; i++) { + + if (i > 0) { + pos += GAP; + } + + BitmapText dot = PixelScene.createText( DOT, 6 ); + dot.x = MARGIN; + dot.y = pos; + if (dotWidth == 0) { + dot.measure(); + dotWidth = dot.width(); + } + add( dot ); + + BitmapTextMultiline item = PixelScene.createMultiline( items[i], 6 ); + item.x = dot.x + dotWidth; + item.y = pos; + item.maxWidth = (int)(WIDTH - MARGIN * 2 - dotWidth); + item.measure(); + add( item ); + + pos += item.height(); + float w = item.width(); + if (w > maxWidth) { + maxWidth = w; + } + } + + resize( (int)(maxWidth + dotWidth + MARGIN * 2), (int)(pos + MARGIN) ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndMessage.java b/src/com/watabou/pixeldungeon/windows/WndMessage.java new file mode 100644 index 0000000000..3d8833e3bf --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndMessage.java @@ -0,0 +1,43 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class WndMessage extends Window { + + private static final int WIDTH = 120; + private static final int MARGIN = 4; + + public WndMessage( String text ) { + + super(); + + BitmapTextMultiline info = PixelScene.createMultiline( text, 6 ); + info.maxWidth = WIDTH - MARGIN * 2; + info.measure(); + info.x = info.y = MARGIN; + add( info ); + + resize( + (int)info.width() + MARGIN * 2, + (int)info.height() + MARGIN * 2 ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndOptions.java b/src/com/watabou/pixeldungeon/windows/WndOptions.java new file mode 100644 index 0000000000..db236db1de --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndOptions.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; + +public class WndOptions extends Window { + + private static final int WIDTH = 120; + private static final int MARGIN = 2; + private static final int BUTTON_HEIGHT = 20; + + public WndOptions( String title, String message, String... options ) { + super(); + + BitmapTextMultiline tfTitle = PixelScene.createMultiline( title, 9 ); + tfTitle.hardlight( TITLE_COLOR ); + tfTitle.x = tfTitle.y = MARGIN; + tfTitle.maxWidth = WIDTH - MARGIN * 2; + tfTitle.measure(); + add( tfTitle ); + + BitmapTextMultiline tfMesage = PixelScene.createMultiline( message, 8 ); + tfMesage.maxWidth = WIDTH - MARGIN * 2; + tfMesage.measure(); + tfMesage.x = MARGIN; + tfMesage.y = tfTitle.y + tfTitle.height() + MARGIN; + add( tfMesage ); + + float pos = tfMesage.y + tfMesage.height() + MARGIN; + + for (int i=0; i < options.length; i++) { + final int index = i; + RedButton btn = new RedButton( options[i] ) { + @Override + protected void onClick() { + hide(); + onSelect( index ); + } + }; + btn.setRect( MARGIN, pos, WIDTH - MARGIN * 2, BUTTON_HEIGHT ); + add( btn ); + + pos += BUTTON_HEIGHT + MARGIN; + } + + resize( WIDTH, (int)pos ); + } + + protected void onSelect( int index ) {}; +} diff --git a/src/com/watabou/pixeldungeon/windows/WndQuest.java b/src/com/watabou/pixeldungeon/windows/WndQuest.java new file mode 100644 index 0000000000..d9c068c818 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndQuest.java @@ -0,0 +1,28 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.pixeldungeon.actors.mobs.Mob; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndQuest extends WndTitledMessage { + + public WndQuest( Mob.NPC questgiver, String text ) { + super( questgiver.sprite(), Utils.capitalize( questgiver.name ), text ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndRanking.java b/src/com/watabou/pixeldungeon/windows/WndRanking.java new file mode 100644 index 0000000000..13cdcdc836 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndRanking.java @@ -0,0 +1,345 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import java.util.Locale; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.Game; +import com.watabou.noosa.Group; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Badges; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.hero.Belongings; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.HeroSprite; +import com.watabou.pixeldungeon.ui.BadgesList; +import com.watabou.pixeldungeon.ui.Icons; +import com.watabou.pixeldungeon.ui.ItemSlot; +import com.watabou.pixeldungeon.ui.ScrollPane; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndRanking extends WndTabbed { + + private static final String TXT_ERROR = "Unable to load additional information"; + + private static final String TXT_STATS = "Stats"; + private static final String TXT_ITEMS = "Items"; + private static final String TXT_BADGES = "Badges"; + + private static final int WIDTH = 112; + private static final int HEIGHT = 144; + + private static final int TAB_WIDTH = 40; + + private Thread thread; + private String error = null; + + private Image busy; + + public WndRanking( final String gameFile ) { + + super(); + resize( WIDTH, HEIGHT ); + + thread = new Thread() { + @Override + public void run() { + try { + Badges.loadGlobal(); + Dungeon.loadGame( gameFile ); + } catch (Exception e ) { + error = TXT_ERROR; + } + } + }; + thread.start(); + + busy = Icons.BUSY.get(); + busy.origin.set( busy.width / 2, busy.height / 2 ); + busy.angularSpeed = 720; + busy.x = (WIDTH - busy.width) / 2; + busy.y = (HEIGHT - busy.height) / 2; + add( busy ); + } + + @Override + public void update() { + super.update(); + + if (thread != null && !thread.isAlive()) { + thread = null; + if (error == null) { + remove( busy ); + createControls(); + } else { + hide(); + Game.scene().add( new WndError( TXT_ERROR ) ); + } + } + } + + private void createControls() { + + String[] labels = + {TXT_STATS, TXT_ITEMS, TXT_BADGES}; + Group[] pages = + {new StatsTab(), new ItemsTab(), new BadgesTab()}; + + for (int i=0; i < pages.length; i++) { + + add( pages[i] ); + + Tab tab = new RankingTab( labels[i], pages[i] ); + tab.setSize( TAB_WIDTH, tabHeight() ); + add( tab ); + } + + select( 0 ); + } + + private class RankingTab extends LabeledTab { + + private Group page; + + public RankingTab( String label, Group page ) { + super( label ); + this.page = page; + } + + @Override + protected void select( boolean value ) { + super.select( value ); + if (page != null) { + page.visible = page.active = selected; + } + } + } + + private class StatsTab extends Group { + + private static final int GAP = 4; + + private static final String TXT_TITLE = "Level %d %s"; + + private static final String TXT_HEALTH = "Health"; + private static final String TXT_STR = "Strength"; + + private static final String TXT_DURATION = "Game Duration"; + + private static final String TXT_DEPTH = "Maximum Depth"; + private static final String TXT_ENEMIES = "Mobs Killed"; + private static final String TXT_GOLD = "Gold Collected"; + + private static final String TXT_FOOD = "Food Eaten"; + private static final String TXT_ALCHEMY = "Potions Cooked"; + private static final String TXT_ANKHS = "Ankhs Used"; + + public StatsTab() { + super(); + + String heroClass = Dungeon.hero.className(); + + IconTitle title = new IconTitle(); + title.icon( HeroSprite.avatar( Dungeon.hero.heroClass, Dungeon.hero.tier() ) ); + title.label( Utils.format( TXT_TITLE, Dungeon.hero.lvl, heroClass ).toUpperCase( Locale.ENGLISH ) ); + title.setRect( 0, 0, WIDTH, 0 ); + add( title ); + + float pos = title.bottom() + GAP + GAP; + + pos = statSlot( this, TXT_STR, Integer.toString( Dungeon.hero.STR ), pos ); + pos = statSlot( this, TXT_HEALTH, Integer.toString( Dungeon.hero.HT ), pos ); + + pos += GAP; + + pos = statSlot( this, TXT_DURATION, Integer.toString( (int)Statistics.duration ), pos ); + + pos += GAP; + + pos = statSlot( this, TXT_DEPTH, Integer.toString( Statistics.deepestFloor ), pos ); + pos = statSlot( this, TXT_ENEMIES, Integer.toString( Statistics.enemiesSlain ), pos ); + pos = statSlot( this, TXT_GOLD, Integer.toString( Statistics.goldCollected ), pos ); + + pos += GAP; + + pos = statSlot( this, TXT_FOOD, Integer.toString( Statistics.foodEaten ), pos ); + pos = statSlot( this, TXT_ALCHEMY, Integer.toString( Statistics.potionsCooked ), pos ); + pos = statSlot( this, TXT_ANKHS, Integer.toString( Statistics.ankhsUsed ), pos ); + } + + private float statSlot( Group parent, String label, String value, float pos ) { + + BitmapText txt = PixelScene.createText( label, 7 ); + txt.y = pos; + parent.add( txt ); + + txt = PixelScene.createText( value, 7 ); + txt.measure(); + txt.x = PixelScene.align( WIDTH * 0.65f ); + txt.y = pos; + parent.add( txt ); + + return pos + GAP + txt.baseLine(); + } + } + + private class ItemsTab extends Group { + + private float pos; + + public ItemsTab() { + super(); + + Belongings stuff = Dungeon.hero.belongings; + if (stuff.weapon != null) { + addItem( stuff.weapon ); + } + if (stuff.armor != null) { + addItem( stuff.armor ); + } + if (stuff.ring1 != null) { + addItem( stuff.ring1 ); + } + if (stuff.ring2 != null) { + addItem( stuff.ring2 ); + } + + if (Dungeon.quickslot instanceof Item && + Dungeon.hero.belongings.backpack.contains( (Item)Dungeon.quickslot )) { + + addItem( (Item)Dungeon.quickslot ); + } else if (Dungeon.quickslot instanceof Class){ + @SuppressWarnings("unchecked") + Item item = Dungeon.hero.belongings.getItem( (Class)Dungeon.quickslot ); + if (item != null) { + addItem( item ); + } + } + } + + private void addItem( Item item ) { + ItemButton slot = new ItemButton( item ); + slot.setRect( 0, pos, width, ItemButton.HEIGHT ); + add( slot ); + + pos += slot.height() + 1; + } + } + + private class BadgesTab extends Group { + + public BadgesTab() { + super(); + + camera = WndRanking.this.camera; + + ScrollPane list = new BadgesList( false ); + add( list ); + + list.setSize( WIDTH, HEIGHT ); + } + } + + private class ItemButton extends Button { + + public static final int HEIGHT = 28; + + private Item item; + + private ItemSlot slot; + private ColorBlock bg; + private BitmapText name; + + public ItemButton( Item item ) { + + super(); + + this.item = item; + + slot.item( item ); + if (item.cursed && item.cursedKnown) { + bg.ra = +0.2f; + bg.ga = -0.1f; + } else if (!item.isIdentified()) { + bg.ra = 0.1f; + bg.ba = 0.1f; + } + } + + @Override + protected void createChildren() { + + bg = new ColorBlock( HEIGHT, HEIGHT, 0xFF4A4D44 ); + add( bg ); + + slot = new ItemSlot(); + add( slot ); + + name = PixelScene.createText( "?", 7 ); + add( name ); + + super.createChildren(); + } + + @Override + protected void layout() { + bg.x = x; + bg.y = y; + + slot.setRect( x, y, HEIGHT, HEIGHT ); + + name.x = slot.right() + 2; + name.y = y + (height - name.baseLine()) / 2; + + String str = Utils.capitalize( item.name() ); + name.text( str ); + name.measure(); + if (name.width() > width - name.x) { + do { + str = str.substring( 0, str.length() - 1 ); + name.text( str + "..." ); + name.measure(); + } while (name.width() > width - name.x); + } + + super.layout(); + } + + @Override + protected void onTouchDown() { + bg.brightness( 1.5f ); + Sample.INSTANCE.play( Assets.SND_CLICK, 0.7f, 0.7f, 1.2f ); + }; + + protected void onTouchUp() { + bg.brightness( 1.0f ); + }; + + @Override + protected void onClick() { + Game.scene().add( new WndItem( null, item ) ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndResurrect.java b/src/com/watabou/pixeldungeon/windows/WndResurrect.java new file mode 100644 index 0000000000..8a8ce7fe1f --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndResurrect.java @@ -0,0 +1,102 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Game; +import com.watabou.pixeldungeon.Rankings; +import com.watabou.pixeldungeon.Statistics; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.items.Ankh; +import com.watabou.pixeldungeon.scenes.InterlevelScene; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; + +public class WndResurrect extends Window { + + private static final String TXT_MESSAGE = "You died, but you were given another chance to win this dungeon. Will you take it?"; + private static final String TXT_YES = "Yes, I will fight!"; + private static final String TXT_NO = "No, I give up"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + private static final float GAP = 2; + + public static WndResurrect instance; + public static Object causeOfDeath; + + public WndResurrect( final Ankh ankh, Object causeOfDeath ) { + + super(); + + instance = this; + WndResurrect.causeOfDeath = causeOfDeath; + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( ankh.image(), null ) ); + titlebar.label( ankh.name() ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline message = PixelScene.createMultiline( TXT_MESSAGE, 6 ); + message.maxWidth = WIDTH; + message.measure(); + message.y = titlebar.bottom() + GAP; + add( message ); + + RedButton btnYes = new RedButton( TXT_YES ) { + @Override + protected void onClick() { + hide(); + + Statistics.ankhsUsed++; + + InterlevelScene.mode = InterlevelScene.Mode.RESURRECT; + Game.switchScene( InterlevelScene.class ); + } + }; + btnYes.setRect( 0, message.y + message.height() + GAP, WIDTH, BTN_HEIGHT ); + add( btnYes ); + + RedButton btnNo = new RedButton( TXT_NO ) { + @Override + protected void onClick() { + hide(); + + Rankings.INSTANCE.submit( false ); + Hero.reallyDie( WndResurrect.causeOfDeath ); + } + }; + btnNo.setRect( 0, btnYes.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnNo ); + + resize( WIDTH, (int)btnNo.bottom() ); + } + + @Override + public void destroy() { + super.destroy(); + instance = null; + } + + @Override + public void onBackPressed() { + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndSadGhost.java b/src/com/watabou/pixeldungeon/windows/WndSadGhost.java new file mode 100644 index 0000000000..35287fede4 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndSadGhost.java @@ -0,0 +1,104 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Ghost; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.quest.DriedRose; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndSadGhost extends Window { + + private static final String TXT_ROSE = + "Yes! Yes!!! This is it! Please give it to me! " + + "And you can take one of these items, maybe they " + + "will be useful to you in your journey..."; + private static final String TXT_RAT = + "Yes! The ugly creature is slain and I can finally rest... " + + "Please take one of these items, maybe they " + + "will be useful to you in your journey..."; + private static final String TXT_WEAPON = "Ghost's weapon"; + private static final String TXT_ARMOR = "Ghost's armor"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + private static final float GAP = 2; + + public WndSadGhost( final Ghost ghost, final Item item ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( item.image(), null ) ); + titlebar.label( Utils.capitalize( item.name() ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline message = PixelScene.createMultiline( item instanceof DriedRose ? TXT_ROSE : TXT_RAT, 6 ); + message.maxWidth = WIDTH; + message.measure(); + message.y = titlebar.bottom() + GAP; + add( message ); + + RedButton btnWeapon = new RedButton( TXT_WEAPON ) { + @Override + protected void onClick() { + selectReward( ghost, item, Ghost.Quest.weapon ); + } + }; + btnWeapon.setRect( 0, message.y + message.height() + GAP, WIDTH, BTN_HEIGHT ); + add( btnWeapon ); + + RedButton btnArmor = new RedButton( TXT_ARMOR ) { + @Override + protected void onClick() { + selectReward( ghost, item, Ghost.Quest.armor ); + } + }; + btnArmor.setRect( 0, btnWeapon.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnArmor ); + + resize( WIDTH, (int)btnArmor.bottom() ); + } + + private void selectReward( Ghost ghost, Item item, Item reward ) { + + hide(); + + item.detach( Dungeon.hero.belongings.backpack ); + + if (reward.doPickUp( Dungeon.hero )) { + GLog.i( Hero.TXT_YOU_NOW_HAVE, reward.name() ); + } else { + Dungeon.level.drop( reward, ghost.pos ).sprite.drop(); + } + + ghost.yell( "Farewell, adventurer!" ); + ghost.die( null ); + + Ghost.Quest.complete(); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndSettings.java b/src/com/watabou/pixeldungeon/windows/WndSettings.java new file mode 100644 index 0000000000..f1a3b0b24f --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndSettings.java @@ -0,0 +1,171 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.Camera; +import com.watabou.noosa.audio.Sample; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.PixelDungeon; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.CheckBox; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; + +public class WndSettings extends Window { + + private static final String TXT_ZOOM_IN = "+"; + private static final String TXT_ZOOM_OUT = "-"; + private static final String TXT_ZOOM_DEFAULT = "Default Zoom"; + + private static final String TXT_SCALE_UP = "Scale up UI"; + + private static final String TXT_MUSIC = "Music"; + + private static final String TXT_SOUND = "Sound FX"; + + private static final String TXT_BRIGHTNESS = "Brightness"; + + private static final String TXT_SWITCH_PORT = "Switch to portrait"; + private static final String TXT_SWITCH_LAND = "Switch to landscape"; + + private static final int WIDTH = 112; + private static final int BTN_HEIGHT = 20; + private static final int GAP = 2; + + private RedButton btnZoomOut; + private RedButton btnZoomIn; + + public WndSettings( boolean inGame ) { + super(); + + if (inGame) { + int w = BTN_HEIGHT; + + // Zoom out + btnZoomOut = new RedButton( TXT_ZOOM_OUT ) { + @Override + protected void onClick() { + zoom( Camera.main.zoom - 1 ); + } + }; + add( btnZoomOut.setRect( 0, 0, w, BTN_HEIGHT) ); + + // Zoom in + btnZoomIn = new RedButton( TXT_ZOOM_IN ) { + @Override + protected void onClick() { + zoom( Camera.main.zoom + 1 ); + } + }; + add( btnZoomIn.setRect( WIDTH - w, 0, w, BTN_HEIGHT) ); + + // Default zoom + add( new RedButton( TXT_ZOOM_DEFAULT ) { + @Override + protected void onClick() { + zoom( PixelScene.defaultZoom ); + } + }.setRect( btnZoomOut.right(), 0, WIDTH - btnZoomIn.width() - btnZoomOut.width(), BTN_HEIGHT ) ); + + } else { + + CheckBox btnScaleUp = new CheckBox( TXT_SCALE_UP ) { + @Override + protected void onClick() { + super.onClick(); + PixelDungeon.scaleUp( checked() ); + } + }; + btnScaleUp.setRect( 0, 0, WIDTH, BTN_HEIGHT ); + btnScaleUp.checked( PixelDungeon.scaleUp() ); + add( btnScaleUp ); + + } + + CheckBox btnMusic = new CheckBox( TXT_MUSIC ) { + @Override + protected void onClick() { + super.onClick(); + PixelDungeon.music( checked() ); + } + }; + btnMusic.setRect( 0, BTN_HEIGHT + GAP, WIDTH, BTN_HEIGHT ); + btnMusic.checked( PixelDungeon.music() ); + add( btnMusic ); + + CheckBox btnSound = new CheckBox( TXT_SOUND ) { + @Override + protected void onClick() { + super.onClick(); + PixelDungeon.soundFx( checked() ); + Sample.INSTANCE.play( Assets.SND_CLICK ); + } + }; + btnSound.setRect( 0, btnMusic.bottom() + GAP, WIDTH, BTN_HEIGHT ); + btnSound.checked( PixelDungeon.soundFx() ); + add( btnSound ); + + if (!inGame) { + + RedButton btnOrientation = new RedButton( orientationText() ) { + @Override + protected void onClick() { + PixelDungeon.landscape( !PixelDungeon.landscape() ); + } + }; + btnOrientation.setRect( 0, btnSound.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnOrientation ); + + resize( WIDTH, (int)btnOrientation.bottom() ); + + } else { + + CheckBox btnBrightness = new CheckBox( TXT_BRIGHTNESS ) { + @Override + protected void onClick() { + super.onClick(); + PixelDungeon.brightness( checked() ); + } + }; + btnBrightness.setRect( 0, btnSound.bottom() + GAP, WIDTH, BTN_HEIGHT ); + btnBrightness.checked( PixelDungeon.brightness() ); + add( btnBrightness ); + + resize( WIDTH, (int)btnBrightness.bottom() ); + + } + } + + private void zoom( float value ) { + + Camera.main.zoom( value ); + PixelDungeon.zoom( (int)(value - PixelScene.defaultZoom) ); + + updateEnabled(); + } + + private void updateEnabled() { + float zoom = Camera.main.zoom; + btnZoomIn.enable( zoom < PixelScene.maxZoom ); + btnZoomOut.enable( zoom > PixelScene.minZoom ); + } + + private String orientationText() { + return PixelDungeon.landscape() ? TXT_SWITCH_PORT : TXT_SWITCH_LAND; + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndStory.java b/src/com/watabou/pixeldungeon/windows/WndStory.java new file mode 100644 index 0000000000..526d8106eb --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndStory.java @@ -0,0 +1,133 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.input.Touchscreen.Touch; +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Game; +import com.watabou.noosa.TouchArea; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.utils.SparseArray; + +public class WndStory extends Window { + + private static final int WIDTH = 120; + private static final int MARGIN = 6; + + private static final float bgR = 0.77f; + private static final float bgG = 0.73f; + private static final float bgB = 0.62f; + + public static final int ID_SEWERS = 0; + public static final int ID_PRISON = 1; + public static final int ID_CAVES = 2; + public static final int ID_METROPOLIS = 3; + public static final int ID_HALLS = 4; + + private static final SparseArray CHAPTERS = new SparseArray(); + + static { + CHAPTERS.put( ID_SEWERS, + "The Dungeon lies right beneath the City, its upper levels actually constitute the City's sewer system. " + + "Being nominally a part of the City, these levels are not that dangerous. No one will call it a safe place, " + + "but at least you won't need to deal with evil magic here." ); + + CHAPTERS.put( ID_PRISON, + "Many years ago an underground prison was built here for the most dangerous criminals. At the time it seemed " + + "like a very clever idea, because this place indeed was very hard to escape. But soon dark miasma started to permeate " + + "from below, driving prisoners and guards insane. In the end the prison was abandoned, though some convicts " + + "were left locked up here." ); + + CHAPTERS.put( ID_CAVES, + "The caves, which stretch down under the abandoned prison, are sparcely populated. They lie too deep to be exploited " + + "by the City and they are too poor in minerals to interest the dwarves. In the past there was a trade outpost " + + "somewhere here on the route between these two states, but it has perished since the decline of Dwarven Metropolis. " + + "Only omnipresent gnolls and subterranean animals dwell here now." ); + + CHAPTERS.put( ID_METROPOLIS, + "Dwarven Metropolis was once the greatest of dwarven city-states. In its heyday the mechanized army of dwarves " + + "has successfully repelled the invasion of the old god and his demon army. But it is said, that the returning warriors " + + "have brought seeds of corruption with them, and that victory was the beginning of the end for the underground kingdom." ); + + CHAPTERS.put( ID_HALLS, + "In the past these levels were the outskirts of Metropolis. After the costly victory in the war with the old god " + + "dwarves were too weakened to clear them of remaining demons. Gradually demons have tightened their grip on this place " + + "and now it's called Demon Halls.\n\n" + + "Very few adventurers have ever descended this far..." ); + }; + + private BitmapTextMultiline tf; + + private float delay; + + public WndStory( String text ) { + super( 0, 0, Chrome.get( Chrome.Type.SCROLL ) ); + + tf = PixelScene.createMultiline( text, 7 ); + tf.maxWidth = WIDTH - MARGIN * 2; + tf.measure(); + tf.ra = bgR; + tf.ga = bgG; + tf.ba = bgB; + tf.rm = -bgR; + tf.gm = -bgG; + tf.bm = -bgB; + tf.x = MARGIN; + add( tf ); + + add( new TouchArea( chrome ) { + @Override + protected void onClick( Touch touch ) { + hide(); + } + } ); + + resize( (int)(tf.width() + MARGIN * 2), (int)Math.min( tf.height(), 180 ) ); + } + + @Override + public void update() { + super.update(); + + if (delay > 0 && (delay -= Game.elapsed) <= 0) { + chrome.visible = tf.visible = true; + } + } + + public static void showChapter( int id ) { + + if (Dungeon.chapters.contains( id )) { + return; + } + + String text = CHAPTERS.get( id ); + if (text != null) { + WndStory wnd = new WndStory( text ); + if ((wnd.delay = 0.6f) > 0) { + wnd.chrome.visible = wnd.tf.visible = false; + } + + Game.scene().add( wnd ); + + Dungeon.chapters.add( id ); + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndTabbed.java b/src/com/watabou/pixeldungeon/windows/WndTabbed.java new file mode 100644 index 0000000000..18282d95fd --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndTabbed.java @@ -0,0 +1,187 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import java.util.ArrayList; + +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.Game; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.ui.Button; +import com.watabou.pixeldungeon.Assets; +import com.watabou.pixeldungeon.Chrome; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class WndTabbed extends Window { + + protected ArrayList tabs = new ArrayList(); + protected Tab selected; + + public WndTabbed() { + super( 0, 0, Chrome.get( Chrome.Type.TAB_SET ) ); + } + + protected Tab add( Tab tab ) { + + tab.setPos( tabs.size() == 0 ? + -chrome.marginLeft() + 1 : + tabs.get( tabs.size() - 1 ).right(), height ); + tab.select( false ); + super.add( tab ); + + tabs.add( tab ); + + return tab; + } + + public void select( int index ) { + select( tabs.get( index ) ); + } + + public void select( Tab tab ) { + if (tab != selected) { + for (Tab t : tabs) { + if (t == selected) { + t.select( false ); + } else if (t == tab) { + t.select( true ); + } + } + + selected = tab; + } + } + + @Override + public void resize( int w, int h ) { + // -> super.resize(...) + this.width = w; + this.height = h; + + chrome.size( + width + chrome.marginHor(), + height + chrome.marginVer() ); + + camera.resize( (int)chrome.width, (int)(chrome.marginTop() + height + tabHeight()) ); + camera.x = (int)(Game.width - camera.screenWidth()) / 2; + camera.y = (int)(Game.height - camera.screenHeight()) / 2; + // <- super.resize(...) + + for (Tab tab : tabs) { + remove( tab ); + } + + ArrayList tabs = new ArrayList( this.tabs ); + this.tabs.clear(); + + for (Tab tab : tabs) { + add( tab ); + } + } + + protected int tabHeight() { + return 25; + } + + protected void onClick( Tab tab ) { + select( tab ); + } + + protected class Tab extends Button { + + protected final int CUT = 5; + + protected boolean selected; + + protected NinePatch bg; + + @Override + protected void layout() { + super.layout(); + + if (bg != null) { + bg.x = x; + bg.y = y; + bg.size( width, height ); + } + } + + protected void select( boolean value ) { + + active = !(selected = value); + + if (bg != null) { + remove( bg ); + } + + bg = Chrome.get( selected ? + Chrome.Type.TAB_SELECTED : + Chrome.Type.TAB_UNSELECTED ); + addToBack( bg ); + + layout(); + } + + @Override + protected void onClick() { + Sample.INSTANCE.play( Assets.SND_CLICK, 0.7f, 0.7f, 1.2f ); + WndTabbed.this.onClick( this ); + } + } + + protected class LabeledTab extends Tab { + + private BitmapText btLabel; + + public LabeledTab( String label ) { + + super(); + + btLabel.text( label ); + btLabel.measure(); + } + + @Override + protected void createChildren() { + super.createChildren(); + + btLabel = PixelScene.createText( 9 ); + add( btLabel ); + } + + @Override + protected void layout() { + super.layout(); + + btLabel.x = PixelScene.align( x + (width - btLabel.width()) / 2 ); + btLabel.y = PixelScene.align( y + (height - btLabel.baseLine()) / 2 ) - 1; + if (!selected) { + btLabel.y -= 2; + } + } + + @Override + protected void select( boolean value ) { + super.select( value ); + btLabel.am = selected ? 1.0f : 0.6f; + } + } + +} diff --git a/src/com/watabou/pixeldungeon/windows/WndTitledMessage.java b/src/com/watabou/pixeldungeon/windows/WndTitledMessage.java new file mode 100644 index 0000000000..7e94cef8f0 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndTitledMessage.java @@ -0,0 +1,72 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.noosa.Image; +import com.watabou.noosa.ui.Component; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.ui.Window; + +public class WndTitledMessage extends Window { + + private static final int WIDTH = 120; + private static final int GAP = 2; + + private BitmapTextMultiline normal; + private BitmapTextMultiline highlighted; + + public WndTitledMessage( Image icon, String title, String message ) { + + this( new IconTitle( icon, title ), message ); + + } + + public WndTitledMessage( Component titlebar, String message ) { + + super(); + + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + Highlighter hl = new Highlighter( message ); + + normal = PixelScene.createMultiline( hl.text, 6 ); + normal.maxWidth = WIDTH; + normal.measure(); + normal.x = titlebar.left(); + normal.y = titlebar.bottom() + GAP; + add( normal ); + + if (hl.isHighlighted()) { + normal.mask = hl.inverted(); + + highlighted = PixelScene.createMultiline( hl.text, 6 ); + highlighted.maxWidth = normal.maxWidth; + highlighted.measure(); + highlighted.x = normal.x; + highlighted.y = normal.y; + add( highlighted ); + + highlighted.mask = hl.mask; + highlighted.hardlight( TITLE_COLOR ); + } + + resize( WIDTH, (int)(normal.y + normal.height()) ); + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndTradeItem.java b/src/com/watabou/pixeldungeon/windows/WndTradeItem.java new file mode 100644 index 0000000000..e4855975b6 --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndTradeItem.java @@ -0,0 +1,250 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Shopkeeper; +import com.watabou.pixeldungeon.items.EquipableItem; +import com.watabou.pixeldungeon.items.Gold; +import com.watabou.pixeldungeon.items.Heap; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.rings.RingOfHaggler; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.ItemSlot; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndTradeItem extends Window { + + private static final float GAP = 2; + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + + private static final String TXT_SALE = "FOR SALE: %s - %dg"; + private static final String TXT_BUY = "Buy for %dg"; + private static final String TXT_SELL = "Sell for %dg"; + private static final String TXT_SELL_1 = "Sell 1 for %dg"; + private static final String TXT_SELL_ALL = "Sell all for %dg"; + private static final String TXT_CANCEL = "Never mind"; + + private static final String TXT_SOLD = "You've sold your %s for %dg"; + private static final String TXT_BOUGHT = "You've bought %s for %dg"; + + private WndBag owner; + + public WndTradeItem( final Item item, WndBag owner ) { + + super(); + + this.owner = owner; + + float pos = createDescription( item, false ); + + if (item.quantity() == 1) { + + RedButton btnSell = new RedButton( Utils.format( TXT_SELL, item.price() ) ) { + @Override + protected void onClick() { + sell( item ); + hide(); + } + }; + btnSell.setRect( 0, pos + GAP, WIDTH, BTN_HEIGHT ); + add( btnSell ); + + pos = btnSell.bottom(); + + } else { + + int priceAll= item.price(); + RedButton btnSell1 = new RedButton( Utils.format( TXT_SELL_1, priceAll / item.quantity() ) ) { + @Override + protected void onClick() { + sellOne( item ); + hide(); + } + }; + btnSell1.setRect( 0, pos + GAP, WIDTH, BTN_HEIGHT ); + add( btnSell1 ); + RedButton btnSellAll = new RedButton( Utils.format( TXT_SELL_ALL, priceAll ) ) { + @Override + protected void onClick() { + sell( item ); + hide(); + } + }; + btnSellAll.setRect( 0, btnSell1.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnSellAll ); + + pos = btnSellAll.bottom(); + + } + + RedButton btnCancel = new RedButton( TXT_CANCEL ) { + @Override + protected void onClick() { + hide(); + } + }; + btnCancel.setRect( 0, pos + GAP, WIDTH, BTN_HEIGHT ); + add( btnCancel ); + + resize( WIDTH, (int)btnCancel.bottom() ); + } + + public WndTradeItem( final Heap heap, boolean canBuy ) { + + super(); + + Item item = heap.peek(); + + float pos = createDescription( item, true ); + + int price = price( item ); + + if (canBuy) { + + RedButton btnBuy = new RedButton( Utils.format( TXT_BUY, price ) ) { + @Override + protected void onClick() { + hide(); + buy( heap ); + } + }; + btnBuy.setRect( 0, pos + GAP, WIDTH, BTN_HEIGHT ); + btnBuy.enable( price <= Dungeon.gold ); + add( btnBuy ); + + RedButton btnCancel = new RedButton( TXT_CANCEL ) { + @Override + protected void onClick() { + hide(); + } + }; + btnCancel.setRect( 0, btnBuy.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnCancel ); + + resize( WIDTH, (int)btnCancel.bottom() ); + + } else { + + resize( WIDTH, (int)pos ); + + } + } + + @Override + public void hide() { + + super.hide(); + + if (owner != null) { + owner.hide(); + Shopkeeper.sell(); + } + } + + private float createDescription( Item item, boolean forSale ) { + + // Title + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( item.image(), item.glowing() ) ); + titlebar.label( forSale ? + Utils.format( TXT_SALE, item.toString(), price( item ) ) : + Utils.capitalize( item.toString() ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + // Upgraded / degraded + if (item.levelKnown && item.level > 0) { + titlebar.color( ItemSlot.UPGRADED ); + } else if (item.levelKnown && item.level < 0) { + titlebar.color( ItemSlot.DEGRADED ); + } + + // Description + BitmapTextMultiline info = PixelScene.createMultiline( item.info(), 6 ); + info.maxWidth = WIDTH; + info.measure(); + info.x = titlebar.left(); + info.y = titlebar.bottom() + GAP; + add( info ); + + return info.y + info.height(); + } + + private void sell( Item item ) { + + Hero hero = Dungeon.hero; + + if (item.isEquipped( hero ) && !((EquipableItem)item).doUnequip( hero, false )) { + return; + } + item.detachAll( hero.belongings.backpack ); + + int price = item.price(); + + new Gold( price ).doPickUp( hero ); + GLog.i( TXT_SOLD, item.name(), price ); + } + + private void sellOne( Item item ) { + + if (item.quantity() <= 1) { + sell( item ); + } else { + + Hero hero = Dungeon.hero; + + item = item.detach( hero.belongings.backpack ); + int price = item.price(); + + new Gold( price ).doPickUp( hero ); + GLog.i( TXT_SOLD, item.name(), price ); + } + } + + private int price( Item item ) { + // This formula is not completely correct... + int price = item.price() * 5 * (Dungeon.depth / 5 + 1); + if (Dungeon.hero.buff( RingOfHaggler.Haggling.class ) != null && price >= 2) { + price /= 2; + } + return price; + } + + private void buy( Heap heap ) { + + Hero hero = Dungeon.hero; + Item item = heap.pickUp(); + + int price = price( item ); + Dungeon.gold -= price; + + GLog.i( TXT_BOUGHT, item.name(), price ); + + if (!item.doPickUp( hero )) { + Dungeon.level.drop( item, heap.pos ).sprite.drop(); + } + } +} diff --git a/src/com/watabou/pixeldungeon/windows/WndWandmaker.java b/src/com/watabou/pixeldungeon/windows/WndWandmaker.java new file mode 100644 index 0000000000..50e808f67c --- /dev/null +++ b/src/com/watabou/pixeldungeon/windows/WndWandmaker.java @@ -0,0 +1,104 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2014 Oleg Dolya + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ +package com.watabou.pixeldungeon.windows; + +import com.watabou.noosa.BitmapTextMultiline; +import com.watabou.pixeldungeon.Dungeon; +import com.watabou.pixeldungeon.actors.hero.Hero; +import com.watabou.pixeldungeon.actors.mobs.npcs.Wandmaker; +import com.watabou.pixeldungeon.items.Item; +import com.watabou.pixeldungeon.items.wands.Wand; +import com.watabou.pixeldungeon.scenes.PixelScene; +import com.watabou.pixeldungeon.sprites.ItemSprite; +import com.watabou.pixeldungeon.ui.RedButton; +import com.watabou.pixeldungeon.ui.Window; +import com.watabou.pixeldungeon.utils.GLog; +import com.watabou.pixeldungeon.utils.Utils; + +public class WndWandmaker extends Window { + + private static final String TXT_MESSAGE = + "Oh, I see you have succeeded! I do hope it hasn't troubled you too much. " + + "As I promised, you can choose one of my high quality wands."; + private static final String TXT_BATTLE = "Battle wand"; + private static final String TXT_NON_BATTLE = "Non-battle wand"; + + private static final String TXT_FARAWELL = "Good luck in your quest, %s!"; + + private static final int WIDTH = 120; + private static final int BTN_HEIGHT = 18; + private static final float GAP = 2; + + public WndWandmaker( final Wandmaker wandmaker, final Item item ) { + + super(); + + IconTitle titlebar = new IconTitle(); + titlebar.icon( new ItemSprite( item.image(), null ) ); + titlebar.label( Utils.capitalize( item.name() ) ); + titlebar.setRect( 0, 0, WIDTH, 0 ); + add( titlebar ); + + BitmapTextMultiline message = PixelScene.createMultiline( TXT_MESSAGE, 6 ); + message.maxWidth = WIDTH; + message.measure(); + message.y = titlebar.bottom() + GAP; + add( message ); + + RedButton btnBattle = new RedButton( TXT_BATTLE ) { + @Override + protected void onClick() { + selectReward( wandmaker, item, Wandmaker.Quest.wand1 ); + } + }; + btnBattle.setRect( 0, message.y + message.height() + GAP, WIDTH, BTN_HEIGHT ); + add( btnBattle ); + + RedButton btnNonBattle = new RedButton( TXT_NON_BATTLE ) { + @Override + protected void onClick() { + selectReward( wandmaker, item, Wandmaker.Quest.wand2 ); + } + }; + btnNonBattle.setRect( 0, btnBattle.bottom() + GAP, WIDTH, BTN_HEIGHT ); + add( btnNonBattle ); + + resize( WIDTH, (int)btnNonBattle.bottom() ); + } + + private void selectReward( Wandmaker wandmaker, Item item, Wand reward ) { + + hide(); + + item.detach( Dungeon.hero.belongings.backpack ); + + reward.identify(); + if (reward.doPickUp( Dungeon.hero )) { + GLog.i( Hero.TXT_YOU_NOW_HAVE, reward.name() ); + } else { + Dungeon.level.drop( reward, wandmaker.pos ).sprite.drop(); + } + + wandmaker.yell( Utils.format( TXT_FARAWELL, Dungeon.hero.className() ) ); + wandmaker.destroy(); + + wandmaker.sprite.die(); + + Wandmaker.Quest.complete(); + } +}