V0.1.0 Partial Commit

changed package and application names to differentiate from main PD
release
This commit is contained in:
Evan Debenham
2014-08-03 14:46:22 -04:00
parent 65dd9c2dc0
commit aed303672a
474 changed files with 3468 additions and 3458 deletions
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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";
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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.shatteredpixel.shatteredpixeldungeon.actors.mobs.Acidic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Albino;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bandit;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Senior;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Shielded;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.ScrollHolder;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.SeedPouch;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.WandHolster;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfHaggler;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfThorns;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.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<Badge> global;
private static HashSet<Badge> local = new HashSet<Badges.Badge>();
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<Badge> restore( Bundle bundle ) {
HashSet<Badge> badges = new HashSet<Badge>();
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<Badge> 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<Badge>();
}
}
}
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<Badge> filtered( boolean global ) {
HashSet<Badge> filtered = new HashSet<Badge>( global ? Badges.global : Badges.local );
if (!global) {
Iterator<Badge> 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<Badge> list = new ArrayList<Badge>( filtered );
Collections.sort( list );
return list;
}
private static void leaveBest( HashSet<Badge> 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;
}
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.watabou.noosa.Game;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Wandmaker;
import com.shatteredpixel.shatteredpixeldungeon.items.Ankh;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.levels.CavesBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.CavesLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.CityBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.CityLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.DeadEndLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.HallsBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.HallsLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.LastLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.LastShopLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.StartScene;
import com.shatteredpixel.shatteredpixeldungeon.utils.BArray;
import com.shatteredpixel.shatteredpixeldungeon.utils.Utils;
import com.shatteredpixel.shatteredpixeldungeon.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<? extends Item>
public static Object quickslot;
public static int depth;
public static int gold;
// Reason of death
public static String resultDescription;
public static HashSet<Integer> 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<Integer>();
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<Integer>();
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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
import com.watabou.noosa.Image;
import com.watabou.noosa.TextureFilm;
import com.watabou.noosa.Tilemap;
import com.watabou.noosa.tweeners.AlphaTweener;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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.shatteredpixel.shatteredpixeldungeon.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();
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
import java.util.HashMap;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.watabou.utils.Bundle;
public class GamesInProgress {
private static HashMap<HeroClass, Info> state = new HashMap<HeroClass, Info>();
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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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<Record>, 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<Record> records;
public static void reset() {
records = new ArrayList<Journal.Record>();
}
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<Record>();
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;
}
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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();
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.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<Record> 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<Rankings.Record>();
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<Record> scoreComparator = new Comparator<Rankings.Record>() {
@Override
public int compare( Record lhs, Record rhs ) {
return (int)Math.signum( rhs.score - lhs.score );
}
};
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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";
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.TitleScene;
public class ShatteredPixelDungeon extends Game {
public ShatteredPixelDungeon() {
super( TitleScene.class );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade.class,
"com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfEnhancement" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.blobs.WaterOfHealth.class,
"com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Light" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfMending.class,
"com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfRejuvenation" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfTelekinesis.class,
"com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfTelekenesis" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Foliage.class,
"com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blooming" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Shadows.class,
"com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Rejuvenation" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast.class,
"com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfNuclearBlast" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero.class,
"com.shatteredpixel.shatteredpixeldungeon.actors.Hero" );
// com.watabou.utils.Bundle.addAlias(
// com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Javelin.class,
// "com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Boomerang" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Shopkeeper.class,
"com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Shopkeeper" );
// 1.6.1
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.quest.DriedRose.class,
"com.shatteredpixel.shatteredpixeldungeon.items.DriedRose" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.MirrorImage.class,
"com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMirrorImage.MirrorImage" );
// 1.6.4
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.class,
"com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfCleansing" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.class,
"com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfResistance" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Boomerang.class,
"com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.RangersBoomerang" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfPower.class,
"com.shatteredpixel.shatteredpixeldungeon.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 ) );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon;
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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors;
import java.util.Arrays;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.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<Actor> all = new HashSet<Actor>();
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<Actor> all() {
return all;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors;
import java.util.HashSet;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Shadows;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Speed;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Levitation;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MindVision;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Slow;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bestiary;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PoisonParticle;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Door;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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<Buff> buffs = new HashSet<Buff>();
@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<Buff> buffs() {
return buffs;
}
@SuppressWarnings("unchecked")
public <T extends Buff> HashSet<T> buffs( Class<T> c ) {
HashSet<T> filtered = new HashSet<T>();
for (Buff b : buffs) {
if (c.isInstance( b )) {
filtered.add( (T)b );
}
}
return filtered;
}
@SuppressWarnings("unchecked")
public <T extends Buff> T buff( Class<T> 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<? extends Buff> 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<Class<?>> EMPTY = new HashSet<Class<?>>();
public HashSet<Class<?>> resistances() {
return EMPTY;
}
public HashSet<Class<?>> immunities() {
return EMPTY;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import java.util.Arrays;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.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 extends Blob> T seed( int cell, int amount, Class<T> 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) {
ShatteredPixelDungeon.reportException(e);
return null;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlameParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Shadows;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShaftParticle;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SnowParticle;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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.." );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.DungeonTilemap;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.Journal.Feature;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Awareness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Identification;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.Journal.Feature;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hunger;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShaftParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.DewVial;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.Journal.Feature;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator.Category;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfMight;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfStrength;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfWeaponUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon.Enchantment;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.*;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.Journal.Feature;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.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 (Class<?>waterClass : 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;
}
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
public class Amok extends FlavourBuff {
@Override
public int icon() {
return BuffIndicator.AMOK;
}
@Override
public String toString() {
return "Amok";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
public class Awareness extends FlavourBuff {
public static final float DURATION = 2f;
@Override
public void detach() {
super.detach();
Dungeon.observe();
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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 extends Buff> T affect( Char target, Class<T> 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 extends FlavourBuff> T affect( Char target, Class<T> buffClass, float duration ) {
T buff = affect( target, buffClass );
buff.spend( duration );
return buff;
}
public static<T extends FlavourBuff> T prolong( Char target, Class<T> 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<? extends Buff> cl ) {
detach( target.buff( cl ) );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Thief;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.food.ChargrilledMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.food.FrozenCarpaccio;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Paralysis.class );
IMMUNITIES.add( ToxicGas.class );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfSatiety;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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();
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.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();
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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..." );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
public class Sleep extends FlavourBuff {
public static final float SWS = 1.5f;
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
public class SnipersMark extends FlavourBuff {
@Override
public int icon() {
return BuffIndicator.MARK;
}
@Override
public String toString() {
return "Sniper's mark";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
public class Speed extends FlavourBuff {
public static final float DURATION = 10f;
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.hero;
import java.util.Iterator;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.IronKey;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRemoveCurse;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
public class Belongings implements Iterable<Item> {
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 extends Item> T getItem( Class<T> itemClass ) {
for (Item item : this) {
if (itemClass.isInstance( item )) {
return (T)item;
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T extends Key> T getKey( Class<T> 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<Item> iterator() {
return new ItemIterator();
}
private class ItemIterator implements Iterator<Item> {
private int index = 0;
private Iterator<Item> 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();
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.hero;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.TomeOfMastery;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClothArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Food;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfStrength;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfShadows;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfIdentify;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfMagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Dagger;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Knuckles;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.ShortSword;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Dart;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.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;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Leech;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( Leech.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.watabou.utils.Random;
public class Bestiary {
public static Mob mob( int depth ) {
@SuppressWarnings("unchecked")
Class<? extends Mob> cl = (Class<? extends Mob>)mobClass( depth );
try {
return cl.newInstance();
} catch (Exception e) {
return null;
}
}
public static Mob mutable( int depth ) {
@SuppressWarnings("unchecked")
Class<? extends Mob> cl = (Class<? extends Mob>)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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.sprites.BruteSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Terror.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.Camera;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.SkeletonKey;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfThorns;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.DM300Sprite;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( ToxicGas.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfLiquidFlame;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfFirebolt;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Fire;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Burning.class );
IMMUNITIES.add( Fire.class );
IMMUNITIES.add( WandOfFirebolt.class );
IMMUNITIES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PurpleParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Dewdrop;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfDisintegration;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Leech;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.EyeSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( WandOfDisintegration.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( Leech.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Terror.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Amok.class );
IMMUNITIES.add( Terror.class );
IMMUNITIES.add( Sleep.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.Camera;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.LloydsBeacon;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.SkeletonKey;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.GooSprite;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ToxicGas.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.ArmorKit;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.SkeletonKey;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlink;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfDisintegration;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.CityBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.KingSprite;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ToxicGas.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
RESISTANCES.add( WandOfDisintegration.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Paralysis.class );
}
@Override
public HashSet<Class<?>> 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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Death.class );
IMMUNITIES.add( Paralysis.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
import com.shatteredpixel.shatteredpixeldungeon.effects.Wound;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.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<? extends CharSprite> 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<Mob> enemies = new HashSet<Mob>();
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<? extends Item>)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();
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp;
import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Food;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Knuckles;
import com.shatteredpixel.shatteredpixeldungeon.sprites.MonkSprite;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Amok.class );
IMMUNITIES.add( Terror.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Burning.class );
IMMUNITIES.add( Paralysis.class );
IMMUNITIES.add( ToxicGas.class );
IMMUNITIES.add( Roots.class );
IMMUNITIES.add( Frost.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
import com.shatteredpixel.shatteredpixeldungeon.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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Leech;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( Leech.class );
RESISTANCES.add( Poison.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.Camera;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SparkParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.LightningTrap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ShamanSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( LightningTrap.Electricity.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.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 );
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.sprites.SkeletonSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Death.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Web;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( Poison.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Roots.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon.Enchantment;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Leech;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ToxicGas.class );
RESISTANCES.add( Poison.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
IMMUNITIES.add( Leech.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfLullaby;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlink;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Leech;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( Leech.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Sleep.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.ArrayList;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Door;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.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<Integer> candidates = new ArrayList<Integer>();
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.";
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Badges.Badge;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.TomeOfMastery;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.SkeletonKey;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ToxicGas.class );
RESISTANCES.add( Poison.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfHaggler;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ThiefSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Weakness;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.WarlockSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( Death.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet;
import com.watabou.noosa.tweeners.AlphaTweener;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Death.class );
IMMUNITIES.add( Terror.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.ArrayList;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.SkeletonKey;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Death;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.BurningFistSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.LarvaSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.RottingFistSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.YogSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.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<Integer> spawnPoints = new ArrayList<Integer>();
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<Mob>)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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
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<Class<?>> 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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ToxicGas.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Amok.class );
IMMUNITIES.add( Sleep.class );
IMMUNITIES.add( Terror.class );
IMMUNITIES.add( Poison.class );
}
@Override
public HashSet<Class<?>> 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<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static {
RESISTANCES.add( ToxicGas.class );
RESISTANCES.add( Death.class );
RESISTANCES.add( ScrollOfPsionicBlast.class );
}
@Override
public HashSet<Class<?>> resistances() {
return RESISTANCES;
}
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Amok.class );
IMMUNITIES.add( Sleep.class );
IMMUNITIES.add( Terror.class );
IMMUNITIES.add( Burning.class );
}
@Override
public HashSet<Class<?>> 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;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import java.util.Collection;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.items.EquipableItem;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.DarkGold;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.Pickaxe;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.levels.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.Room.Type;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.BlacksmithSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBlacksmith;
import com.shatteredpixel.shatteredpixeldungeon.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<Room> 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;
}
}
}
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import java.util.HashSet;
import com.watabou.noosa.audio.Sample;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ParalyticGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.RatSkull;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.FetidRatSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.GhostSprite;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndQuest;
import com.shatteredpixel.shatteredpixeldungeon.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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Paralysis.class );
IMMUNITIES.add( Roots.class );
}
@Override
public HashSet<Class<?>> 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<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( Paralysis.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Journal;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Golem;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Monk;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.DwarfToken;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.levels.CityLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Room;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ImpSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.Utils;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndImp;
import com.shatteredpixel.shatteredpixeldungeon.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;
}
}
}

Some files were not shown because too many files have changed in this diff Show More