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,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;
}
}
}
@@ -0,0 +1,71 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ImpSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.Utils;
public class ImpShopkeeper extends Shopkeeper {
private static final String TXT_GREETINGS = "Hello, friend!";
{
name = "ambitious imp";
spriteClass = ImpSprite.class;
}
private boolean seenBefore = false;
@Override
protected boolean act() {
if (!seenBefore && Dungeon.visible[pos]) {
yell( Utils.format( TXT_GREETINGS ) );
seenBefore = true;
}
return super.act();
}
@Override
protected void flee() {
for (Heap heap: Dungeon.level.heaps.values()) {
if (heap.type == Heap.Type.FOR_SALE) {
CellEmitter.get( heap.pos ).burst( ElmoParticle.FACTORY, 4 );
heap.destroy();
}
}
destroy();
sprite.emitter().burst( Speck.factory( Speck.WOOL ), 15 );
sprite.killAndErase();
}
@Override
public String description() {
return
"Imps are lesser demons. They are notable for neither their strength nor their magic talent. " +
"But they are quite smart and sociable, and many of imps prefer to live and do business among non-demons.";
}
}
@@ -0,0 +1,138 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.MirrorSprite;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
public class MirrorImage extends Mob.NPC {
{
name = "mirror image";
spriteClass = MirrorSprite.class;
state = State.HUNTING;
enemy = DUMMY;
}
public int tier;
private int attack;
private int damage;
private static final String TIER = "tier";
private static final String ATTACK = "attack";
private static final String DAMAGE = "damage";
@Override
public void storeInBundle( Bundle bundle ) {
super.storeInBundle( bundle );
bundle.put( TIER, tier );
bundle.put( ATTACK, attack );
bundle.put( DAMAGE, damage );
}
@Override
public void restoreFromBundle( Bundle bundle ) {
super.restoreFromBundle( bundle );
tier = bundle.getInt( TIER );
attack = bundle.getInt( ATTACK );
damage = bundle.getInt( DAMAGE );
}
public void duplicate( Hero hero ) {
tier = hero.tier();
attack = hero.attackSkill( hero );
damage = hero.damageRoll();
}
@Override
public int attackSkill( Char target ) {
return attack;
}
@Override
public int damageRoll() {
return damage;
}
@Override
public int attackProc( Char enemy, int damage ) {
int dmg = super.attackProc( enemy, damage );
destroy();
sprite.die();
return dmg;
}
protected Char chooseEnemy() {
if (enemy == DUMMY || !enemy.isAlive()) {
HashSet<Mob> enemies = new HashSet<Mob>();
for (Mob mob:Dungeon.level.mobs) {
if (mob.hostile && Level.fieldOfView[mob.pos]) {
enemies.add( mob );
}
}
enemy = enemies.size() > 0 ? Random.element( enemies ) : DUMMY;
}
return enemy;
}
@Override
public String description() {
return
"This illusion bears a close resemblance to you, " +
"but it's paler and twitches a little.";
}
@Override
public CharSprite sprite() {
CharSprite s = super.sprite();
((MirrorSprite)s).updateArmor( tier );
return s;
}
@Override
public void interact() {
int curPos = pos;
moveSprite( pos, Dungeon.hero.pos );
move( Dungeon.hero.pos );
Dungeon.hero.sprite.move( Dungeon.hero.pos, curPos );
Dungeon.hero.move( curPos );
Dungeon.hero.spend( 1 / Dungeon.hero.speed() );
Dungeon.hero.busy();
}
}
@@ -0,0 +1,82 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.sprites.RatKingSprite;
public class RatKing extends Mob.NPC {
{
name = "rat king";
spriteClass = RatKingSprite.class;
state = State.SLEEPING;
}
@Override
public int defenseSkill( Char enemy ) {
return 1000;
}
@Override
public float speed() {
return 2f;
}
@Override
protected Char chooseEnemy() {
return DUMMY;
}
@Override
public void damage( int dmg, Object src ) {
}
@Override
public void add( Buff buff ) {
}
@Override
public boolean reset() {
return true;
}
@Override
public void interact() {
sprite.turnTo( pos, Dungeon.hero.pos );
if (state == State.SLEEPING) {
notice();
yell( "I'm not sleeping!" );
state = State.WANDERING;
} else {
yell( "What is it? I have no time for this nonsense. My kingdom won't rule itself!" );
}
}
@Override
public String description() {
return
"This rat is a little bigger than a regular marsupial rat " +
"and it's wearing a tiny crown on its head.";
}
}
@@ -0,0 +1,103 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ShopkeeperSprite;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndTradeItem;
public class Shopkeeper extends Mob.NPC {
{
name = "shopkeeper";
spriteClass = ShopkeeperSprite.class;
}
@Override
protected boolean act() {
throwItem();
sprite.turnTo( pos, Dungeon.hero.pos );
spend( TICK );
return true;
}
@Override
public void damage( int dmg, Object src ) {
flee();
}
@Override
public void add( Buff buff ) {
flee();
}
protected void flee() {
for (Heap heap: Dungeon.level.heaps.values()) {
if (heap.type == Heap.Type.FOR_SALE) {
CellEmitter.get( heap.pos ).burst( ElmoParticle.FACTORY, 4 );
heap.destroy();
}
}
destroy();
sprite.killAndErase();
CellEmitter.get( pos ).burst( ElmoParticle.FACTORY, 6 );
}
@Override
public boolean reset() {
return true;
}
@Override
public String description() {
return
"This stout guy looks more appropriate for a trade district in some large city " +
"than for a dungeon. His prices explain why he prefers to do business here.";
}
public static WndBag sell() {
return GameScene.selectItem( itemSelector, WndBag.Mode.FOR_SALE, "Select an item to sell" );
}
private static WndBag.Listener itemSelector = new WndBag.Listener() {
@Override
public void onSelect( Item item ) {
if (item != null) {
WndBag parentWnd = sell();
GameScene.show( new WndTradeItem( item, parentWnd ) );
}
}
};
@Override
public void interact() {
sell();
}
}
@@ -0,0 +1,379 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import java.util.ArrayList;
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.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
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.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfStrength;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.CorpseDust;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfAmok;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfAvalanche;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlink;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfDisintegration;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfFirebolt;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfLightning;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfPoison;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfSlowness;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfTelekinesis;
import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.sprites.WandmakerSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.utils.Utils;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndQuest;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndWandmaker;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
public class Wandmaker extends Mob.NPC {
{
name = "old wandmaker";
spriteClass = WandmakerSprite.class;
}
private static final String TXT_BERRY1 =
"Oh, what a pleasant surprise to meet a decent person in such place! I came here for a rare ingredient - " +
"a _Rotberry seed_. Being a magic user, I'm quite able to defend myself against local monsters, " +
"but I'm getting lost in no time, it's very embarrassing. Probably you could help me? I would be " +
"happy to pay for your service with one of my best wands.";
private static final String TXT_DUST1 =
"Oh, what a pleasant surprise to meet a decent person in such place! I came here for a rare ingredient - " +
"_corpse dust_. It can be gathered from skeletal remains and there is an ample number of them in the dungeon. " +
"Being a magic user, I'm quite able to defend myself against local monsters, but I'm getting lost in no time, " +
"it's very embarrassing. Probably you could help me? I would be happy to pay for your service with one of my best wands.";
private static final String TXT_BERRY2 =
"Any luck with a Rotberry seed, %s? No? Don't worry, I'm not in a hurry.";
private static final String TXT_DUST2 =
"Any luck with corpse dust, %s? Bone piles are the most obvious places to look.";
@Override
protected boolean act() {
throwItem();
return super.act();
}
@Override
public int defenseSkill( Char enemy ) {
return 1000;
}
@Override
public String defenseVerb() {
return "absorbed";
}
@Override
public void damage( int dmg, Object src ) {
}
@Override
public void add( Buff buff ) {
}
@Override
public boolean reset() {
return true;
}
@Override
public void interact() {
sprite.turnTo( pos, Dungeon.hero.pos );
if (Quest.given) {
Item item = Quest.alternative ?
Dungeon.hero.belongings.getItem( CorpseDust.class ) :
Dungeon.hero.belongings.getItem( Rotberry.Seed.class );
if (item != null) {
GameScene.show( new WndWandmaker( this, item ) );
} else {
tell( Quest.alternative ? TXT_DUST2 : TXT_BERRY2, Dungeon.hero.className() );
}
} else {
tell( Quest.alternative ? TXT_DUST1 : TXT_BERRY1 );
Quest.given = true;
Quest.placeItem();
Journal.add( Journal.Feature.WANDMAKER );
}
}
private void tell( String format, Object...args ) {
GameScene.show( new WndQuest( this, Utils.format( format, args ) ) );
}
@Override
public String description() {
return
"This old but hale gentleman wears a slightly confused " +
"expression. He is protected by a magic shield.";
}
public static class Quest {
private static boolean spawned;
private static boolean alternative;
private static boolean given;
public static Wand wand1;
public static Wand wand2;
public static void reset() {
spawned = false;
wand1 = null;
wand2 = null;
}
private static final String NODE = "wandmaker";
private static final String SPAWNED = "spawned";
private static final String ALTERNATIVE = "alternative";
private static final String GIVEN = "given";
private static final String WAND1 = "wand1";
private static final String WAND2 = "wand2";
public static void storeInBundle( Bundle bundle ) {
Bundle node = new Bundle();
node.put( SPAWNED, spawned );
if (spawned) {
node.put( ALTERNATIVE, alternative );
node.put(GIVEN, given );
node.put( WAND1, wand1 );
node.put( WAND2, wand2 );
}
bundle.put( NODE, node );
}
public static void restoreFromBundle( Bundle bundle ) {
Bundle node = bundle.getBundle( NODE );
if (!node.isNull() && (spawned = node.getBoolean( SPAWNED ))) {
alternative = node.getBoolean( ALTERNATIVE );
given = node.getBoolean( GIVEN );
wand1 = (Wand)node.get( WAND1 );
wand2 = (Wand)node.get( WAND2 );
} else {
reset();
}
}
public static void spawn( PrisonLevel level, Room room ) {
if (!spawned && Dungeon.depth > 6 && Random.Int( 10 - Dungeon.depth ) == 0) {
Wandmaker npc = new Wandmaker();
do {
npc.pos = room.random();
} while (level.map[npc.pos] == Terrain.ENTRANCE || level.map[npc.pos] == Terrain.SIGN);
level.mobs.add( npc );
Actor.occupyCell( npc );
spawned = true;
alternative = Random.Int( 2 ) == 0;
given = false;
switch (Random.Int( 5 )) {
case 0:
wand1 = new WandOfAvalanche();
break;
case 1:
wand1 = new WandOfDisintegration();
break;
case 2:
wand1 = new WandOfFirebolt();
break;
case 3:
wand1 = new WandOfLightning();
break;
case 4:
wand1 = new WandOfPoison();
break;
}
wand1.random().upgrade();
switch (Random.Int( 5 )) {
case 0:
wand2 = new WandOfAmok();
break;
case 1:
wand2 = new WandOfBlink();
break;
case 2:
wand2 = new WandOfRegrowth();
break;
case 3:
wand2 = new WandOfSlowness();
break;
case 4:
wand2 = new WandOfTelekinesis();
break;
}
wand2.random().upgrade();
}
}
public static void placeItem() {
if (alternative) {
ArrayList<Heap> candidates = new ArrayList<Heap>();
for (Heap heap : Dungeon.level.heaps.values()) {
if (heap.type == Heap.Type.SKELETON && !Dungeon.visible[heap.pos]) {
candidates.add( heap );
}
}
if (candidates.size() > 0) {
Random.element( candidates ).drop( new CorpseDust() );
} else {
int pos = Dungeon.level.randomRespawnCell();
while (Dungeon.level.heaps.get( pos ) != null) {
pos = Dungeon.level.randomRespawnCell();
}
Heap heap = Dungeon.level.drop( new CorpseDust(), pos );
heap.type = Heap.Type.SKELETON;
heap.sprite.link();
}
} else {
int shrubPos = Dungeon.level.randomRespawnCell();
while (Dungeon.level.heaps.get( shrubPos ) != null) {
shrubPos = Dungeon.level.randomRespawnCell();
}
Dungeon.level.plant( new Rotberry.Seed(), shrubPos );
}
}
public static void complete() {
wand1 = null;
wand2 = null;
Journal.remove( Journal.Feature.WANDMAKER );
}
}
public static class Rotberry extends Plant {
private static final String TXT_DESC =
"Berries of this shrub taste like sweet, sweet death.";
{
image = 7;
plantName = "Rotberry";
}
@Override
public void activate( Char ch ) {
super.activate( ch );
GameScene.add( Blob.seed( pos, 100, ToxicGas.class ) );
Dungeon.level.drop( new Seed(), pos ).sprite.drop();
if (ch != null) {
Buff.prolong( ch, Roots.class, TICK * 3 );
}
}
@Override
public String desc() {
return TXT_DESC;
}
public static class Seed extends Plant.Seed {
{
plantName = "Rotberry";
name = "seed of " + plantName;
image = ItemSpriteSheet.SEED_ROTBERRY;
plantClass = Rotberry.class;
alchemyClass = PotionOfStrength.class;
}
@Override
public boolean collect( Bag container ) {
if (super.collect( container )) {
if (Dungeon.level != null) {
for (Mob mob : Dungeon.level.mobs) {
mob.beckon( Dungeon.hero.pos );
}
GLog.w( "The seed emits a roar that echoes throughout the dungeon!" );
CellEmitter.center( Dungeon.hero.pos ).start( Speck.factory( Speck.SCREAM ), 0.3f, 3 );
Sample.INSTANCE.play( Assets.SND_CHALLENGE );
}
return true;
} else {
return false;
}
}
@Override
public String desc() {
return TXT_DESC;
}
}
}
}