Merging Source v1.7.2: actor changes

This commit is contained in:
Evan Debenham 2014-10-20 23:51:15 -04:00
parent 724338b57f
commit 4a49763309
30 changed files with 980 additions and 682 deletions

View File

@ -131,9 +131,11 @@ public abstract class Actor implements Bundlable {
chars[pos] = null; chars[pos] = null;
} }
protected static void next() { /*protected*/public void next() {
if (current == this) {
current = null; current = null;
} }
}
public static void process() { public static void process() {
@ -167,6 +169,14 @@ public abstract class Actor implements Bundlable {
} }
if (current != null) { if (current != null) {
if (current instanceof Char && ((Char)current).sprite.isMoving) {
// If it's character's turn to act, but its sprite
// is moving, wait till the movement is over
current = null;
break;
}
doNext = current.act(); doNext = current.act();
if (doNext && !Dungeon.hero.isAlive()) { if (doNext && !Dungeon.hero.isAlive()) {
doNext = false; doNext = false;

View File

@ -17,6 +17,7 @@
*/ */
package com.shatteredpixel.shatteredpixeldungeon.actors; package com.shatteredpixel.shatteredpixeldungeon.actors;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.*; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.*;
@ -120,9 +121,9 @@ public abstract class Char extends Actor {
GLog.i( TXT_HIT, name, enemy.name ); GLog.i( TXT_HIT, name, enemy.name );
} }
// Refactoring needed! // FIXME
int dr = this instanceof Hero && ((Hero)this).usingRanged && ((Hero)this).subClass == HeroSubClass.SNIPER ? int dr = this instanceof Hero && ((Hero)this).rangedWeapon != null && ((Hero)this).subClass ==
0 : Random.IntRange( 0, enemy.dr() ); HeroSubClass.SNIPER ? 0 : Random.IntRange( 0, enemy.dr() );
int dmg = damageRoll(); int dmg = damageRoll();
int effectiveDamage = Math.max( dmg - dr, 0 );; int effectiveDamage = Math.max( dmg - dr, 0 );;
@ -368,6 +369,10 @@ public abstract class Char extends Actor {
sprite.showStatus( CharSprite.NEGATIVE, "bleeding" ); sprite.showStatus( CharSprite.NEGATIVE, "bleeding" );
} else if (buff instanceof Vertigo) {
sprite.showStatus( CharSprite.NEGATIVE, "dizzy" );
} else if (buff instanceof Sleep) { } else if (buff instanceof Sleep) {
sprite.idle(); sprite.idle();
} }
@ -441,6 +446,19 @@ public abstract class Char extends Actor {
} }
public void move( int step ) { public void move( int step ) {
if (buff( Vertigo.class ) != null) {
ArrayList<Integer> candidates = new ArrayList<Integer>();
for (int dir : Level.NEIGHBOURS8) {
int p = pos + dir;
if ((Level.passable[p] || Level.avoid[p]) && Actor.findChar( p ) == null) {
candidates.add( p );
}
}
step = Random.element( candidates );
}
if (Dungeon.level.map[pos] == Terrain.OPEN_DOOR) { if (Dungeon.level.map[pos] == Terrain.OPEN_DOOR) {
Door.leave( pos ); Door.leave( pos );
} }

View File

@ -0,0 +1,52 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.blobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
public class ConfusionGas extends Blob {
@Override
protected void evolve() {
super.evolve();
Char ch;
for (int i=0; i < LENGTH; i++) {
if (cur[i] > 0 && (ch = Actor.findChar( i )) != null) {
Buff.prolong( ch, Vertigo.class, Vertigo.duration( ch ) );
}
}
}
@Override
public void use( BlobEmitter emitter ) {
super.use( emitter );
emitter.pour( Speck.factory( Speck.CONFUSION, true ), 0.6f );
}
@Override
public String tileDesc() {
return "A cloud of confusion gas is swirling here.";
}
}

View File

@ -27,6 +27,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.utils.Utils; import com.shatteredpixel.shatteredpixeldungeon.utils.Utils;
import com.watabou.utils.Random;
public class ToxicGas extends Blob implements Hero.Doom { public class ToxicGas extends Blob implements Hero.Doom {
@ -41,8 +42,8 @@ public class ToxicGas extends Blob implements Hero.Doom {
if (cur[i] > 0 && (ch = Actor.findChar( i )) != null) { if (cur[i] > 0 && (ch = Actor.findChar( i )) != null) {
int damage = (ch.HT + levelDamage) / 40; int damage = (ch.HT + levelDamage) / 40;
if (damage < 1) { if (Random.Int( 40 ) < (ch.HT + levelDamage) % 40) {
damage = 1; damage++;
} }
ch.damage( damage, this ); ch.damage( damage, this );

View File

@ -17,7 +17,7 @@
*/ */
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs; package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
// Special kind of buff, that doesn't perform any kind actions //Special kind of buff, that doesn't perform any kind actions
public class FlavourBuff extends Buff { public class FlavourBuff extends Buff {
@Override @Override

View File

@ -41,7 +41,7 @@ public class MagicalSleep extends Buff {
GLog.i("You fall into a deep magical sleep."); GLog.i("You fall into a deep magical sleep.");
} }
else if (target instanceof Mob) else if (target instanceof Mob)
((Mob)target).state = Mob.State.SLEEPING; ((Mob)target).state = ((Mob)target).SLEEPEING;
target.paralysed = true; target.paralysed = true;

View File

@ -30,8 +30,6 @@ import com.watabou.utils.Bundle;
public class Poison extends Buff implements Hero.Doom { public class Poison extends Buff implements Hero.Doom {
public static final int DOT = 2;
protected float left; protected float left;
private static final String LEFT = "left"; private static final String LEFT = "left";
@ -67,7 +65,7 @@ public class Poison extends Buff implements Hero.Doom {
public boolean act() { public boolean act() {
if (target.isAlive()) { if (target.isAlive()) {
target.damage( DOT, this ); target.damage( (int)(left / 3) + 1, this );
spend( TICK ); spend( TICK );
if ((left -= TICK) <= 0) { if ((left -= TICK) <= 0) {

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2012-2014 Oleg Dolya
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements.Resistance;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
public class Vertigo extends FlavourBuff {
public static final float DURATION = 10f;
@Override
public int icon() {
return BuffIndicator.VERTIGO;
}
@Override
public String toString() {
return "Vertigo";
}
public static float duration( Char ch ) {
Resistance r = ch.buff( Resistance.class );
return r != null ? r.durationFactor() * DURATION : DURATION;
}
}

View File

@ -120,12 +120,11 @@ public class Belongings implements Iterable<Item> {
public void countIronKeys() { public void countIronKeys() {
IronKey.curDepthQunatity = 0; IronKey.curDepthQuantity = 0;
for (Item item : backpack) { for (Item item : backpack) {
if (item instanceof IronKey && ((IronKey)item).depth == Dungeon.depth) { if (item instanceof IronKey && ((IronKey)item).depth == Dungeon.depth) {
IronKey.curDepthQunatity = item.quantity(); IronKey.curDepthQuantity++;
return;
} }
} }
} }

View File

@ -20,12 +20,16 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.hero;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions;
import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Drowsy; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Drowsy;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CapeOfThorns; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CapeOfThorns;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.*; import com.shatteredpixel.shatteredpixeldungeon.items.rings.*;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.watabou.noosa.Camera; import com.watabou.noosa.Camera;
import com.watabou.noosa.Game; import com.watabou.noosa.Game;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
@ -143,7 +147,7 @@ public class Hero extends Char {
public boolean restoreHealth = false; public boolean restoreHealth = false;
public boolean usingRanged = false; public MissileWeapon rangedWeapon = null;
public Belongings belongings; public Belongings belongings;
public int STR; public int STR;
@ -224,9 +228,6 @@ public class Hero extends Char {
} }
public static void preview( GamesInProgress.Info info, Bundle bundle ) { public static void preview( GamesInProgress.Info info, Bundle bundle ) {
// Refactoring needed!
Armor armor = (Armor)bundle.get( "armor" );
info.armor = armor == null ? 0 : armor.tier;
info.level = bundle.getInt( LEVEL ); info.level = bundle.getInt( LEVEL );
} }
@ -243,28 +244,27 @@ public class Hero extends Char {
return belongings.armor == null ? 0 : belongings.armor.tier; return belongings.armor == null ? 0 : belongings.armor.tier;
} }
public boolean shoot( Char enemy, Weapon wep ) { public boolean shoot( Char enemy, MissileWeapon wep ) {
// Ugly...
usingRanged = true;
KindOfWeapon curWep = belongings.weapon;
belongings.weapon = wep;
rangedWeapon = wep;
boolean result = attack( enemy ); boolean result = attack( enemy );
rangedWeapon = null;
belongings.weapon = curWep;
usingRanged = false;
return result; return result;
} }
@Override @Override
public int attackSkill( Char target ) { public int attackSkill( Char target ) {
if (belongings.weapon != null) { float accuracy = 1;
return (int) (attackSkill * belongings.weapon.acuracyFactor(this)); if (rangedWeapon != null && Level.distance( pos, target.pos ) == 1) {
accuracy *= 0.5f;
}
KindOfWeapon wep = rangedWeapon != null ? rangedWeapon : belongings.weapon;
if (wep != null) {
return (int)(attackSkill * accuracy * wep.acuracyFactor( this ));
} else { } else {
return attackSkill; return (int)(attackSkill * accuracy);
} }
} }
@ -312,14 +312,15 @@ public class Hero extends Char {
@Override @Override
public int damageRoll() { public int damageRoll() {
KindOfWeapon wep = rangedWeapon != null ? rangedWeapon : belongings.weapon;
int dmg; int dmg;
int bonus = 0; int bonus = 0;
for (Buff buff : buffs( RingOfForce.Force.class )) { for (Buff buff : buffs( RingOfForce.Force.class )) {
bonus += ((RingOfForce.Force)buff).level; bonus += ((RingOfForce.Force)buff).level;
} }
if (belongings.weapon != null) { if (wep != null) {
dmg = belongings.weapon.damageRoll( this ) + bonus; dmg = wep.damageRoll( this ) + bonus;
} else { } else {
int bonusMax = 1 + (int)(bonus * (lvl/5f)); int bonusMax = 1 + (int)(bonus * (lvl/5f));
dmg = Random.NormalIntRange( 1+bonus, Math.max(1+bonus , STR()-9+bonusMax ) ); dmg = Random.NormalIntRange( 1+bonus, Math.max(1+bonus , STR()-9+bonusMax ) );
@ -353,9 +354,10 @@ public class Hero extends Char {
} }
public float attackDelay() { public float attackDelay() {
if (belongings.weapon != null) { KindOfWeapon wep = rangedWeapon != null ? rangedWeapon : belongings.weapon;
if (wep != null) {
return belongings.weapon.speedFactor( this ); return wep.speedFactor( this );
} else { } else {
//Normally putting furor speed on unarmed attacks would be unnecessary //Normally putting furor speed on unarmed attacks would be unnecessary
@ -408,6 +410,7 @@ public class Hero extends Char {
} }
ready(); ready();
return false;
} else { } else {
@ -417,52 +420,52 @@ public class Hero extends Char {
if (curAction instanceof HeroAction.Move) { if (curAction instanceof HeroAction.Move) {
actMove( (HeroAction.Move)curAction ); return actMove( (HeroAction.Move)curAction );
} else } else
if (curAction instanceof HeroAction.Interact) { if (curAction instanceof HeroAction.Interact) {
actInteract( (HeroAction.Interact)curAction ); return actInteract( (HeroAction.Interact)curAction );
} else } else
if (curAction instanceof HeroAction.Buy) { if (curAction instanceof HeroAction.Buy) {
actBuy( (HeroAction.Buy)curAction ); return actBuy( (HeroAction.Buy)curAction );
}else }else
if (curAction instanceof HeroAction.PickUp) { if (curAction instanceof HeroAction.PickUp) {
actPickUp( (HeroAction.PickUp)curAction ); return actPickUp( (HeroAction.PickUp)curAction );
} else } else
if (curAction instanceof HeroAction.OpenChest) { if (curAction instanceof HeroAction.OpenChest) {
actOpenChest( (HeroAction.OpenChest)curAction ); return actOpenChest( (HeroAction.OpenChest)curAction );
} else } else
if (curAction instanceof HeroAction.Unlock) { if (curAction instanceof HeroAction.Unlock) {
actUnlock( (HeroAction.Unlock)curAction ); return actUnlock((HeroAction.Unlock) curAction);
} else } else
if (curAction instanceof HeroAction.Descend) { if (curAction instanceof HeroAction.Descend) {
actDescend( (HeroAction.Descend)curAction ); return actDescend( (HeroAction.Descend)curAction );
} else } else
if (curAction instanceof HeroAction.Ascend) { if (curAction instanceof HeroAction.Ascend) {
actAscend( (HeroAction.Ascend)curAction ); return actAscend( (HeroAction.Ascend)curAction );
} else } else
if (curAction instanceof HeroAction.Attack) { if (curAction instanceof HeroAction.Attack) {
actAttack( (HeroAction.Attack)curAction ); return actAttack( (HeroAction.Attack)curAction );
} else } else
if (curAction instanceof HeroAction.Cook) { if (curAction instanceof HeroAction.Cook) {
actCook( (HeroAction.Cook)curAction ); return actCook( (HeroAction.Cook)curAction );
} }
} }
@ -495,40 +498,47 @@ public class Hero extends Char {
act(); act();
} }
private void actMove( HeroAction.Move action ) { private boolean actMove( HeroAction.Move action ) {
if (getCloser( action.dst )) { if (getCloser( action.dst )) {
return true;
} else { } else {
if (Dungeon.level.map[pos] == Terrain.SIGN) { if (Dungeon.level.map[pos] == Terrain.SIGN) {
GameScene.show( new WndMessage( Dungeon.tip() ) ); GameScene.show( new WndMessage( Dungeon.tip() ) );
} }
ready(); ready();
return false;
} }
} }
private void actInteract( HeroAction.Interact action ) { private boolean actInteract( HeroAction.Interact action ) {
Mob.NPC npc = action.npc; NPC npc = action.npc;
if (Level.adjacent( pos, npc.pos )) { if (Level.adjacent( pos, npc.pos )) {
ready(); ready();
sprite.turnTo( pos, npc.pos ); sprite.turnTo( pos, npc.pos );
npc.interact(); npc.interact();
return false;
} else { } else {
if (Level.fieldOfView[npc.pos] && getCloser( npc.pos )) { if (Level.fieldOfView[npc.pos] && getCloser( npc.pos )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
} }
private void actBuy( HeroAction.Buy action ) { private boolean actBuy( HeroAction.Buy action ) {
int dst = action.dst; int dst = action.dst;
if (pos == dst || Level.adjacent( pos, dst )) { if (pos == dst || Level.adjacent( pos, dst )) {
@ -539,28 +549,37 @@ public class Hero extends Char {
GameScene.show( new WndTradeItem( heap, true ) ); GameScene.show( new WndTradeItem( heap, true ) );
} }
return false;
} else if (getCloser( dst )) { } else if (getCloser( dst )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actCook( HeroAction.Cook action ) { private boolean actCook( HeroAction.Cook action ) {
int dst = action.dst; int dst = action.dst;
if (Dungeon.visible[dst]) { if (Dungeon.visible[dst]) {
ready(); ready();
AlchemyPot.operate( this, dst ); AlchemyPot.operate( this, dst );
return false;
} else if (getCloser( dst )) { } else if (getCloser( dst )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actPickUp( HeroAction.PickUp action ) { private boolean actPickUp( HeroAction.PickUp action ) {
int dst = action.dst; int dst = action.dst;
if (pos == dst) { if (pos == dst) {
@ -599,14 +618,19 @@ public class Hero extends Char {
ready(); ready();
} }
return false;
} else if (getCloser( dst )) { } else if (getCloser( dst )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actOpenChest( HeroAction.OpenChest action ) { private boolean actOpenChest( HeroAction.OpenChest action ) {
int dst = action.dst; int dst = action.dst;
if (Level.adjacent( pos, dst ) || pos == dst) { if (Level.adjacent( pos, dst ) || pos == dst) {
@ -624,7 +648,7 @@ public class Hero extends Char {
if (theKey == null) { if (theKey == null) {
GLog.w( TXT_LOCKED_CHEST ); GLog.w( TXT_LOCKED_CHEST );
ready(); ready();
return; return false;
} }
} }
@ -646,14 +670,19 @@ public class Hero extends Char {
ready(); ready();
} }
return false;
} else if (getCloser( dst )) { } else if (getCloser( dst )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actUnlock( HeroAction.Unlock action ) { private boolean actUnlock( HeroAction.Unlock action ) {
int doorCell = action.dst; int doorCell = action.dst;
if (Level.adjacent( pos, doorCell )) { if (Level.adjacent( pos, doorCell )) {
@ -682,14 +711,19 @@ public class Hero extends Char {
ready(); ready();
} }
return false;
} else if (getCloser( doorCell )) { } else if (getCloser( doorCell )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actDescend( HeroAction.Descend action ) { private boolean actDescend( HeroAction.Descend action ) {
int stairs = action.dst; int stairs = action.dst;
if (pos == stairs && pos == Dungeon.level.exit) { if (pos == stairs && pos == Dungeon.level.exit) {
@ -703,14 +737,19 @@ public class Hero extends Char {
InterlevelScene.mode = InterlevelScene.Mode.DESCEND; InterlevelScene.mode = InterlevelScene.Mode.DESCEND;
Game.switchScene( InterlevelScene.class ); Game.switchScene( InterlevelScene.class );
return false;
} else if (getCloser( stairs )) { } else if (getCloser( stairs )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actAscend( HeroAction.Ascend action ) { private boolean actAscend( HeroAction.Ascend action ) {
int stairs = action.dst; int stairs = action.dst;
if (pos == stairs && pos == Dungeon.level.entrance) { if (pos == stairs && pos == Dungeon.level.entrance) {
@ -720,6 +759,7 @@ public class Hero extends Char {
GameScene.show( new WndMessage( TXT_LEAVE ) ); GameScene.show( new WndMessage( TXT_LEAVE ) );
ready(); ready();
} else { } else {
Dungeon.win( ResultDescriptions.WIN );
Dungeon.deleteGame( Dungeon.hero.heroClass, true ); Dungeon.deleteGame( Dungeon.hero.heroClass, true );
Game.switchScene( SurfaceScene.class ); Game.switchScene( SurfaceScene.class );
} }
@ -737,14 +777,19 @@ public class Hero extends Char {
Game.switchScene( InterlevelScene.class ); Game.switchScene( InterlevelScene.class );
} }
return false;
} else if (getCloser( stairs )) { } else if (getCloser( stairs )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
private void actAttack( HeroAction.Attack action ) { private boolean actAttack( HeroAction.Attack action ) {
enemy = action.target; enemy = action.target;
@ -753,12 +798,17 @@ public class Hero extends Char {
spend( attackDelay() ); spend( attackDelay() );
sprite.attack( enemy.pos ); sprite.attack( enemy.pos );
return false;
} else { } else {
if (Level.fieldOfView[enemy.pos] && getCloser( enemy.pos )) { if (Level.fieldOfView[enemy.pos] && getCloser( enemy.pos )) {
return true;
} else { } else {
ready(); ready();
return false;
} }
} }
@ -774,20 +824,20 @@ public class Hero extends Char {
@Override @Override
public int attackProc( Char enemy, int damage ) { public int attackProc( Char enemy, int damage ) {
if (belongings.weapon != null) { KindOfWeapon wep = rangedWeapon != null ? rangedWeapon : belongings.weapon;
if (wep != null) {
KindOfWeapon weapon = belongings.weapon; wep.proc( this, enemy, damage );
weapon.proc( this, enemy, damage );
switch (subClass) { switch (subClass) {
case GLADIATOR: case GLADIATOR:
if (weapon instanceof MeleeWeapon) { if (wep instanceof MeleeWeapon) {
damage += Buff.affect( this, Combo.class ).hit( enemy, damage ); damage += Buff.affect( this, Combo.class ).hit( enemy, damage );
} }
break; break;
case BATTLEMAGE: case BATTLEMAGE:
if (weapon instanceof Wand) { if (wep instanceof Wand) {
Wand wand = (Wand)weapon; Wand wand = (Wand)wep;
if (wand.curCharges < wand.maxCharges && damage > 0) { if (wand.curCharges < wand.maxCharges && damage > 0) {
wand.curCharges++; wand.curCharges++;
@ -800,7 +850,7 @@ public class Hero extends Char {
damage += wand.curCharges; damage += wand.curCharges;
} }
case SNIPER: case SNIPER:
if (usingRanged) { if (rangedWeapon != null) {
Buff.prolong( enemy, SnipersMark.class, attackDelay() * 1.1f ); Buff.prolong( enemy, SnipersMark.class, attackDelay() * 1.1f );
} }
break; break;
@ -859,7 +909,6 @@ public class Hero extends Char {
} }
private void checkVisibleMobs() { private void checkVisibleMobs() {
ArrayList<Mob> visible = new ArrayList<Mob>(); ArrayList<Mob> visible = new ArrayList<Mob>();
boolean newMob = false; boolean newMob = false;
@ -886,7 +935,7 @@ public class Hero extends Char {
} }
public Mob visibleEnemy( int index ) { public Mob visibleEnemy( int index ) {
return visibleEnemies.get( index % visibleEnemies.size() ); return visibleEnemies.get(index % visibleEnemies.size());
} }
private boolean getCloser( final int target ) { private boolean getCloser( final int target ) {
@ -926,8 +975,9 @@ public class Hero extends Char {
if (step != -1) { if (step != -1) {
sprite.move( pos, step ); int oldPos = pos;
move( step ); move(step);
sprite.move(oldPos, pos);
spend( 1 / speed() ); spend( 1 / speed() );
return true; return true;
@ -940,10 +990,10 @@ public class Hero extends Char {
} }
public void handle( int cell ) { public boolean handle( int cell ) {
if (cell == -1) { if (cell == -1) {
return; return false;
} }
Char ch; Char ch;
@ -956,8 +1006,8 @@ public class Hero extends Char {
} else } else
if (Level.fieldOfView[cell] && (ch = Actor.findChar( cell )) instanceof Mob) { if (Level.fieldOfView[cell] && (ch = Actor.findChar( cell )) instanceof Mob) {
if (ch instanceof Mob.NPC) { if (ch instanceof NPC) {
curAction = new HeroAction.Interact( (Mob.NPC)ch ); curAction = new HeroAction.Interact( (NPC)ch );
} else { } else {
curAction = new HeroAction.Attack( ch ); curAction = new HeroAction.Attack( ch );
} }
@ -996,7 +1046,7 @@ public class Hero extends Char {
} }
act(); return act();
} }
public void earnExp( int exp ) { public void earnExp( int exp ) {
@ -1091,6 +1141,9 @@ public class Hero extends Char {
if (((RingOfMight.Might)buff).level > 0) { if (((RingOfMight.Might)buff).level > 0) {
HT += ((RingOfMight.Might) buff).level * 5; HT += ((RingOfMight.Might) buff).level * 5;
} }
} else if (buff instanceof Vertigo) {
GLog.w("Everything is spinning around you!");
interrupt();
} }
else if (buff instanceof Light) { else if (buff instanceof Light) {
@ -1202,18 +1255,17 @@ public class Hero extends Char {
if (!flying) { if (!flying) {
if (Level.water[step]) { if (Level.water[pos]) {
Sample.INSTANCE.play( Assets.SND_WATER, 1, 1, Random.Float( 0.8f, 1.25f ) ); Sample.INSTANCE.play( Assets.SND_WATER, 1, 1, Random.Float( 0.8f, 1.25f ) );
} else { } else {
Sample.INSTANCE.play( Assets.SND_STEP ); Sample.INSTANCE.play( Assets.SND_STEP );
} }
Dungeon.level.press(step, this); Dungeon.level.press(pos, this);
} }
} }
@Override @Override
public void onMotionComplete() { public void onMotionComplete() {
Dungeon.observe(); Dungeon.observe();
search( false ); search( false );
@ -1274,22 +1326,10 @@ public class Hero extends Char {
int positive = 0; int positive = 0;
int negative = 0; int negative = 0;
//holding onto this code for now as it may be useful in coding the Talisman of Foresight.
/*
for (Buff buff : buffs( RingOfDetection.Detection.class )) {
int bonus = ((RingOfDetection.Detection)buff).level;
if (bonus > positive) {
positive = bonus;
} else if (bonus < 0) {
negative += bonus;
}
}
*/
int distance = 1 + positive + negative; int distance = 1 + positive + negative;
float level = intentional ? (2 * awareness - awareness * awareness) : awareness; float level = intentional ? (2 * awareness - awareness * awareness) : awareness;
if (distance <= 0) { if (distance <= 0) {
level /= 2 - distance; level /= 2 - distance;
distance = 1; distance = 1;
} }

View File

@ -18,7 +18,7 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.hero; package com.shatteredpixel.shatteredpixeldungeon.actors.hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC;
public class HeroAction { public class HeroAction {
@ -50,8 +50,8 @@ public class HeroAction {
} }
public static class Interact extends HeroAction { public static class Interact extends HeroAction {
public Mob.NPC npc; public NPC npc;
public Interact( Mob.NPC npc ) { public Interact( NPC npc ) {
this.npc = npc; this.npc = npc;
} }
} }

View File

@ -82,6 +82,8 @@ public enum HeroClass {
hero.heroClass = this; hero.heroClass = this;
initCommon( hero );
switch (this) { switch (this) {
case WARRIOR: case WARRIOR:
initWarrior( hero ); initWarrior( hero );
@ -100,20 +102,37 @@ public enum HeroClass {
break; break;
} }
if (Badges.isUnlocked( masteryBadge() )) {
new TomeOfMastery().collect();
}
hero.updateAwareness(); hero.updateAwareness();
} }
private static void initCommon( Hero hero ) {
(hero.belongings.armor = new ClothArmor()).identify();
new Food().identify().collect();
}
public Badges.Badge masteryBadge() {
switch (this) {
case WARRIOR:
return Badges.Badge.MASTERY_WARRIOR;
case MAGE:
return Badges.Badge.MASTERY_MAGE;
case ROGUE:
return Badges.Badge.MASTERY_ROGUE;
case HUNTRESS:
return Badges.Badge.MASTERY_HUNTRESS;
}
return null;
}
private static void initWarrior( Hero hero ) { private static void initWarrior( Hero hero ) {
hero.STR = hero.STR + 1; hero.STR = hero.STR + 1;
(hero.belongings.weapon = new ShortSword()).identify(); (hero.belongings.weapon = new ShortSword()).identify();
(hero.belongings.armor = new ClothArmor()).identify();
new Dart( 8 ).identify().collect(); new Dart( 8 ).identify().collect();
new Food().identify().collect();
if (Badges.isUnlocked( Badges.Badge.MASTERY_WARRIOR )) {
new TomeOfMastery().collect();
}
Dungeon.quickslot = Dart.class; Dungeon.quickslot = Dart.class;
@ -122,16 +141,10 @@ public enum HeroClass {
private static void initMage( Hero hero ) { private static void initMage( Hero hero ) {
(hero.belongings.weapon = new Knuckles()).identify(); (hero.belongings.weapon = new Knuckles()).identify();
(hero.belongings.armor = new ClothArmor()).identify();
new Food().identify().collect();
WandOfMagicMissile wand = new WandOfMagicMissile(); WandOfMagicMissile wand = new WandOfMagicMissile();
wand.identify().collect(); wand.identify().collect();
if (Badges.isUnlocked( Badges.Badge.MASTERY_MAGE )) {
new TomeOfMastery().collect();
}
Dungeon.quickslot = wand; Dungeon.quickslot = wand;
new ScrollOfIdentify().setKnown(); new ScrollOfIdentify().setKnown();
@ -139,17 +152,13 @@ public enum HeroClass {
private static void initRogue( Hero hero ) { private static void initRogue( Hero hero ) {
(hero.belongings.weapon = new Dagger()).identify(); (hero.belongings.weapon = new Dagger()).identify();
(hero.belongings.armor = new ClothArmor()).identify();
CloakOfShadows cloak = new CloakOfShadows(); CloakOfShadows cloak = new CloakOfShadows();
hero.belongings.misc1 = cloak; hero.belongings.misc1 = cloak;
new Dart( 8 ).identify().collect();
new Food().identify().collect();
hero.belongings.misc1.activate( hero ); hero.belongings.misc1.activate( hero );
if (Badges.isUnlocked( Badges.Badge.MASTERY_ROGUE )) { new Dart( 8 ).identify().collect();
new TomeOfMastery().collect();
}
Dungeon.quickslot = cloak; Dungeon.quickslot = cloak;
@ -161,14 +170,8 @@ public enum HeroClass {
hero.HP = (hero.HT -= 5); hero.HP = (hero.HT -= 5);
(hero.belongings.weapon = new Dagger()).identify(); (hero.belongings.weapon = new Dagger()).identify();
(hero.belongings.armor = new ClothArmor()).identify();
Boomerang boomerang = new Boomerang(); Boomerang boomerang = new Boomerang();
boomerang.identify().collect(); boomerang.identify().collect();
new Food().identify().collect();
if (Badges.isUnlocked( Badges.Badge.MASTERY_HUNTRESS )) {
new TomeOfMastery().collect();
}
Dungeon.quickslot = boomerang; Dungeon.quickslot = boomerang;
} }

View File

@ -26,7 +26,6 @@ public enum HeroSubClass {
GLADIATOR( "gladiator", GLADIATOR( "gladiator",
"A successful attack with a melee weapon allows the _Gladiator_ to start a combo, " + "A successful attack with a melee weapon allows the _Gladiator_ to start a combo, " +
"in which every next successful hit inflicts more damage." ), "in which every next successful hit inflicts more damage." ),
BERSERKER( "berserker", BERSERKER( "berserker",
"When severely wounded, the _Berserker_ enters a state of wild fury " + "When severely wounded, the _Berserker_ enters a state of wild fury " +
"significantly increasing his damage output." ), "significantly increasing his damage output." ),
@ -34,14 +33,12 @@ public enum HeroSubClass {
WARLOCK( "warlock", WARLOCK( "warlock",
"After killing an enemy the _Warlock_ consumes its soul. " + "After killing an enemy the _Warlock_ consumes its soul. " +
"It heals his wounds and satisfies his hunger." ), "It heals his wounds and satisfies his hunger." ),
BATTLEMAGE( "battlemage", BATTLEMAGE( "battlemage",
"When fighting with a wand in his hands, the _Battlemage_ inflicts additional damage depending " + "When fighting with a wand in his hands, the _Battlemage_ inflicts additional damage depending " +
"on the current number of charges. Every successful hit restores 1 charge to this wand." ), "on the current number of charges. Every successful hit restores 1 charge to this wand." ),
ASSASSIN( "assassin", ASSASSIN( "assassin",
"When performing a surprise attack, the _Assassin_ inflicts additional damage to his target." ), "When performing a surprise attack, the _Assassin_ inflicts additional damage to his target." ),
FREERUNNER( "freerunner", FREERUNNER( "freerunner",
"The _Freerunner_ can move almost twice faster, than most of the monsters. When he " + "The _Freerunner_ can move almost twice faster, than most of the monsters. When he " +
"is running, the Freerunner is much harder to hit. For that he must be unencumbered and not starving." ), "is running, the Freerunner is much harder to hit. For that he must be unencumbered and not starving." ),
@ -49,7 +46,6 @@ public enum HeroSubClass {
SNIPER( "sniper", SNIPER( "sniper",
"_Snipers_ are able to detect weak points in an enemy's armor, " + "_Snipers_ are able to detect weak points in an enemy's armor, " +
"effectively ignoring it when using a missile weapon." ), "effectively ignoring it when using a missile weapon." ),
WARDEN( "warden", WARDEN( "warden",
"Having a strong connection with forces of nature gives _Wardens_ an ability to gather dewdrops and " + "Having a strong connection with forces of nature gives _Wardens_ an ability to gather dewdrops and " +
"seeds from plants. Also trampling a high grass grants them a temporary armor buff." ); "seeds from plants. Also trampling a high grass grants them a temporary armor buff." );

View File

@ -246,7 +246,7 @@ public class King extends Mob {
EXP = 0; EXP = 0;
state = State.WANDERING; state = WANDERING;
} }
@Override @Override

View File

@ -20,6 +20,7 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import java.util.HashSet; import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Challenges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
@ -31,7 +32,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.effects.Wound; import com.shatteredpixel.shatteredpixeldungeon.effects.Wound;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfAccuracy; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfAccuracy;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfWealth; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfWealth;
@ -49,14 +49,12 @@ public abstract class Mob extends Char {
protected static final String TXT_RAGE = "#$%^"; protected static final String TXT_RAGE = "#$%^";
protected static final String TXT_EXP = "%+dEXP"; protected static final String TXT_EXP = "%+dEXP";
public enum State { public AiState SLEEPEING = new Sleeping();
SLEEPING, public AiState HUNTING = new Hunting();
HUNTING, public AiState WANDERING = new Wandering();
WANDERING, public AiState FLEEING = new Fleeing();
FLEEING, public AiState PASSIVE = new Passive();
PASSIVE public AiState state = SLEEPEING;
}
public State state = State.SLEEPING;
public Class<? extends CharSprite> spriteClass; public Class<? extends CharSprite> spriteClass;
@ -91,11 +89,18 @@ public abstract class Mob extends Char {
super.storeInBundle( bundle ); super.storeInBundle( bundle );
bundle.put( STATE, state.toString() ); if (state == SLEEPEING) {
bundle.put( SEEN, enemySeen); bundle.put( STATE, Sleeping.TAG );
if (state != State.SLEEPING) { } else if (state == WANDERING) {
bundle.put( TARGET, target ); bundle.put( STATE, Wandering.TAG );
} else if (state == HUNTING) {
bundle.put( STATE, Hunting.TAG );
} else if (state == FLEEING) {
bundle.put( STATE, Fleeing.TAG );
} else if (state == PASSIVE) {
bundle.put( STATE, Passive.TAG );
} }
bundle.put( TARGET, target );
} }
@Override @Override
@ -103,11 +108,20 @@ public abstract class Mob extends Char {
super.restoreFromBundle( bundle ); super.restoreFromBundle( bundle );
state = State.valueOf( bundle.getString( STATE ) ); String state = bundle.getString( STATE );
enemySeen = bundle.getBoolean( SEEN ); if (state.equals( Sleeping.TAG )) {
if (state != State.SLEEPING) { this.state = SLEEPEING;
target = bundle.getInt( TARGET ); } else if (state.equals( Wandering.TAG )) {
this.state = WANDERING;
} else if (state.equals( Hunting.TAG )) {
this.state = HUNTING;
} else if (state.equals( Fleeing.TAG )) {
this.state = FLEEING;
} else if (state.equals( Passive.TAG )) {
this.state = PASSIVE;
} }
target = bundle.getInt( TARGET );
} }
public CharSprite sprite() { public CharSprite sprite() {
@ -124,7 +138,7 @@ public abstract class Mob extends Char {
super.act(); super.act();
boolean alertedNow = alerted; boolean justAlerted = alerted;
alerted = false; alerted = false;
sprite.hideAlert(); sprite.hideAlert();
@ -139,109 +153,7 @@ public abstract class Mob extends Char {
boolean enemyInFOV = enemy.isAlive() && Level.fieldOfView[enemy.pos] && enemy.invisible <= 0; boolean enemyInFOV = enemy.isAlive() && Level.fieldOfView[enemy.pos] && enemy.invisible <= 0;
int oldPos = pos; return state.act( enemyInFOV, justAlerted );
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() { protected Char chooseEnemy() {
@ -272,14 +184,11 @@ public abstract class Mob extends Char {
return Dungeon.hero; return Dungeon.hero;
} }
protected void nowhereToRun() {
}
protected boolean moveSprite( int from, int to ) { protected boolean moveSprite( int from, int to ) {
if (sprite.isVisible() && (Dungeon.visible[from] || Dungeon.visible[to])) { if (sprite.isVisible() && (Dungeon.visible[from] || Dungeon.visible[to])) {
sprite.move( from, to ); sprite.move( from, to );
return false; return true;
} else { } else {
sprite.place( to ); sprite.place( to );
return true; return true;
@ -293,11 +202,11 @@ public abstract class Mob extends Char {
if (sprite != null) { if (sprite != null) {
sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE ); sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE );
} }
state = State.HUNTING; state = HUNTING;
} else if (buff instanceof Terror) { } else if (buff instanceof Terror) {
state = State.FLEEING; state = FLEEING;
} else if (buff instanceof Sleep) { } else if (buff instanceof Sleep) {
state = State.SLEEPING; state = SLEEPEING;
this.sprite().showSleep(); this.sprite().showSleep();
postpone( Sleep.SWS ); postpone( Sleep.SWS );
} }
@ -308,7 +217,7 @@ public abstract class Mob extends Char {
super.remove( buff ); super.remove( buff );
if (buff instanceof Terror) { if (buff instanceof Terror) {
sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE ); sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE );
state = State.HUNTING; state = HUNTING;
} }
} }
@ -409,8 +318,8 @@ public abstract class Mob extends Char {
Terror.recover( this ); Terror.recover( this );
if (state == State.SLEEPING) { if (state == SLEEPEING) {
state = State.WANDERING; state = WANDERING;
} }
alerted = true; alerted = true;
@ -501,8 +410,8 @@ public abstract class Mob extends Char {
notice(); notice();
if (state != State.HUNTING) { if (state != HUNTING) {
state = State.WANDERING; state = WANDERING;
} }
target = cell; target = cell;
} }
@ -519,31 +428,177 @@ public abstract class Mob extends Char {
GLog.n( "%s: \"%s\" ", name, str ); GLog.n( "%s: \"%s\" ", name, str );
} }
public static abstract class NPC extends Mob { public interface AiState {
public boolean act( boolean enemyInFOV, boolean justAlerted );
{ public String status();
HP = HT = 1;
EXP = 0;
hostile = false;
state = State.PASSIVE;
} }
protected void throwItem() { private class Sleeping implements AiState {
Heap heap = Dungeon.level.heaps.get( pos );
if (heap != null) { public static final String TAG = "SLEEPING";
int n;
do { @Override
n = pos + Level.NEIGHBOURS8[Random.Int( 8 )]; public boolean act( boolean enemyInFOV, boolean justAlerted ) {
} while (!Level.passable[n] && !Level.avoid[n]); if (enemyInFOV && Random.Int( distance( enemy ) + enemy.stealth() + (enemy.flying ? 2 : 0) ) == 0) {
Dungeon.level.drop( heap.pickUp(), n ).sprite.drop( pos );
enemySeen = true;
notice();
state = HUNTING;
target = enemy.pos;
if (Dungeon.isChallenged( Challenges.SWARM_INTELLIGENCE )) {
for (Mob mob : Dungeon.level.mobs) {
if (mob != Mob.this) {
mob.beckon( target );
}
}
}
spend( TIME_TO_WAKE_UP );
} else {
enemySeen = false;
spend( TICK );
}
return true;
}
@Override
public String status() {
return String.format( "This %s is sleeping", name );
}
}
private class Wandering implements AiState {
public static final String TAG = "WANDERING";
@Override
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
if (enemyInFOV && (justAlerted || Random.Int( distance( enemy ) / 2 + enemy.stealth() ) == 0)) {
enemySeen = true;
notice();
state = HUNTING;
target = enemy.pos;
} else {
enemySeen = false;
int oldPos = pos;
if (target != -1 && getCloser( target )) {
spend( 1 / speed() );
return moveSprite( oldPos, pos );
} else {
target = Dungeon.level.randomDestination();
spend( TICK );
}
}
return true;
}
@Override
public String status() {
return String.format( "This %s is wandering", name );
}
}
private class Hunting implements AiState {
public static final String TAG = "HUNTING";
@Override
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
enemySeen = enemyInFOV;
if (enemyInFOV && canAttack( enemy )) {
return doAttack( enemy );
} else {
if (enemyInFOV) {
target = enemy.pos;
}
int oldPos = pos;
if (target != -1 && getCloser( target )) {
spend( 1 / speed() );
return moveSprite( oldPos, pos );
} else {
spend( TICK );
state = WANDERING;
target = Dungeon.level.randomDestination();
return true;
}
} }
} }
@Override @Override
public void beckon( int cell ) { public String status() {
return String.format( "This %s is hunting", name );
}
} }
abstract public void interact(); protected class Fleeing implements AiState {
public static final String TAG = "FLEEING";
@Override
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
enemySeen = enemyInFOV;
if (enemyInFOV) {
target = enemy.pos;
}
int oldPos = pos;
if (target != -1 && getFurther( target )) {
spend( 1 / speed() );
return moveSprite( oldPos, pos );
} else {
spend( TICK );
nowhereToRun();
return true;
}
}
protected void nowhereToRun() {
}
@Override
public String status() {
return String.format( "This %s is fleeing", name );
}
}
private class Passive implements AiState {
public static final String TAG = "PASSIVE";
@Override
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
enemySeen = false;
spend( TICK );
return true;
}
@Override
public String status() {
return String.format( "This %s is passive", name );
}
} }
} }

View File

@ -81,7 +81,7 @@ public class Scorpio extends Mob {
@Override @Override
protected boolean getCloser( int target ) { protected boolean getCloser( int target ) {
if (state == State.HUNTING) { if (state == HUNTING) {
return enemySeen && getFurther( target ); return enemySeen && getFurther( target );
} else { } else {
return super.getCloser( target ); return super.getCloser( target );

View File

@ -45,24 +45,17 @@ public class Spinner extends Mob {
loot = new MysteryMeat(); loot = new MysteryMeat();
lootChance = 0.125f; lootChance = 0.125f;
FLEEING = new Fleeing();
} }
@Override @Override
public int damageRoll() { public int damageRoll() {
return Random.NormalIntRange( 12, 16 ); return Random.NormalIntRange(12, 16);
} }
@Override @Override
protected void nowhereToRun() { public int attackSkill(Char target) {
if (buff( Terror.class ) == null) {
state = State.HUNTING;
} else {
super.nowhereToRun();
}
}
@Override
public int attackSkill( Char target ) {
return 20; return 20;
} }
@ -75,30 +68,30 @@ public class Spinner extends Mob {
protected boolean act() { protected boolean act() {
boolean result = super.act(); boolean result = super.act();
if (state == State.FLEEING && buff( Terror.class ) == null && if (state == FLEEING && buff(Terror.class) == null &&
enemySeen && enemy.buff( Poison.class ) == null) { enemySeen && enemy.buff(Poison.class) == null) {
state = State.HUNTING; state = HUNTING;
} }
return result; return result;
} }
@Override @Override
public int attackProc( Char enemy, int damage ) { public int attackProc(Char enemy, int damage) {
if (Random.Int( 2 ) == 0) { if (Random.Int(2) == 0) {
Buff.affect( enemy, Poison.class ).set( Random.Int( 5, 7 ) * Poison.durationFactor( enemy ) ); Buff.affect(enemy, Poison.class).set(Random.Int(7, 9) * Poison.durationFactor(enemy));
state = State.FLEEING; state = FLEEING;
} }
return damage; return damage;
} }
@Override @Override
public void move( int step ) { public void move(int step) {
if (state == State.FLEEING) { if (state == FLEEING) {
GameScene.add( Blob.seed( pos, Random.Int( 5, 7 ), Web.class ) ); GameScene.add(Blob.seed(pos, Random.Int(5, 7), Web.class));
} }
super.move( step ); super.move(step);
} }
@Override @Override
@ -109,8 +102,9 @@ public class Spinner extends Mob {
} }
private static final HashSet<Class<?>> RESISTANCES = new HashSet<Class<?>>(); private static final HashSet<Class<?>> RESISTANCES = new HashSet<Class<?>>();
static { static {
RESISTANCES.add( Poison.class ); RESISTANCES.add(Poison.class);
} }
@Override @Override
@ -119,12 +113,24 @@ public class Spinner extends Mob {
} }
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>(); private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static { static {
IMMUNITIES.add( Roots.class ); IMMUNITIES.add(Roots.class);
} }
@Override @Override
public HashSet<Class<?>> immunities() { public HashSet<Class<?>> immunities() {
return IMMUNITIES; return IMMUNITIES;
} }
private class Fleeing extends Mob.Fleeing {
@Override
protected void nowhereToRun() {
if (buff(Terror.class) == null) {
state = HUNTING;
} else {
super.nowhereToRun();
}
}
}
} }

View File

@ -42,7 +42,7 @@ public class Statue extends Mob {
spriteClass = StatueSprite.class; spriteClass = StatueSprite.class;
EXP = 0; EXP = 0;
state = State.PASSIVE; state = PASSIVE;
} }
private Weapon weapon; private Weapon weapon;
@ -106,8 +106,8 @@ public class Statue extends Mob {
@Override @Override
public void damage( int dmg, Object src ) { public void damage( int dmg, Object src ) {
if (state == State.PASSIVE) { if (state == PASSIVE) {
state = State.HUNTING; state = HUNTING;
} }
super.damage( dmg, src ); super.damage( dmg, src );
@ -137,7 +137,7 @@ public class Statue extends Mob {
@Override @Override
public boolean reset() { public boolean reset() {
state = State.PASSIVE; state = PASSIVE;
return true; return true;
} }

View File

@ -91,7 +91,7 @@ public class Swarm extends Mob {
Swarm clone = split(); Swarm clone = split();
clone.HP = (HP - damage) / 2; clone.HP = (HP - damage) / 2;
clone.pos = Random.element( candidates ); clone.pos = Random.element( candidates );
clone.state = State.HUNTING; clone.state = clone.HUNTING;
if (Dungeon.level.map[clone.pos] == Terrain.DOOR) { if (Dungeon.level.map[clone.pos] == Terrain.DOOR) {
Door.enter( clone.pos ); Door.enter( clone.pos );

View File

@ -50,6 +50,8 @@ public class Thief extends Mob {
loot = MasterThievesArmband.class; loot = MasterThievesArmband.class;
lootChance = 0.01f; lootChance = 0.01f;
FLEEING = new Fleeing();
} }
private static final String ITEM = "item"; private static final String ITEM = "item";
@ -76,16 +78,6 @@ public class Thief extends Mob {
return 0.5f; 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 @Override
public void die( Object cause ) { public void die( Object cause ) {
@ -109,7 +101,7 @@ public class Thief extends Mob {
@Override @Override
public int attackProc( Char enemy, int damage ) { public int attackProc( Char enemy, int damage ) {
if (item == null && enemy instanceof Hero && steal( (Hero)enemy )) { if (item == null && enemy instanceof Hero && steal( (Hero)enemy )) {
state = State.FLEEING; state = FLEEING;
} }
return damage; return damage;
@ -117,7 +109,7 @@ public class Thief extends Mob {
@Override @Override
public int defenseProc(Char enemy, int damage) { public int defenseProc(Char enemy, int damage) {
if (state == State.FLEEING) { if (state == FLEEING) {
Dungeon.level.drop( new Gold(), pos ).sprite.drop(); Dungeon.level.drop( new Gold(), pos ).sprite.drop();
} }
@ -153,4 +145,16 @@ public class Thief extends Mob {
return desc; return desc;
} }
private class Fleeing extends Mob.Fleeing {
@Override
protected void nowhereToRun() {
if (buff( Terror.class ) == null) {
sprite.showStatus( CharSprite.NEGATIVE, TXT_RAGE );
state = HUNTING;
} else {
super.nowhereToRun();
}
}
}
} }

View File

@ -86,7 +86,7 @@ public class Wraith extends Mob {
@Override @Override
public boolean reset() { public boolean reset() {
state = State.WANDERING; state = WANDERING;
return true; return true;
} }
@ -112,7 +112,7 @@ public class Wraith extends Mob {
Wraith w = new Wraith(); Wraith w = new Wraith();
w.adjustStats( Dungeon.depth ); w.adjustStats( Dungeon.depth );
w.pos = pos; w.pos = pos;
w.state = State.HUNTING; w.state = w.HUNTING;
GameScene.add( w, SPAWN_DELAY ); GameScene.add( w, SPAWN_DELAY );
w.sprite.alpha( 0 ); w.sprite.alpha( 0 );

View File

@ -62,7 +62,7 @@ public class Yog extends Mob {
EXP = 50; EXP = 50;
state = State.PASSIVE; state = PASSIVE;
} }
private static final String TXT_DESC = private static final String TXT_DESC =
@ -193,7 +193,7 @@ public class Yog extends Mob {
EXP = 0; EXP = 0;
state = State.WANDERING; state = WANDERING;
} }
public RottingFist() { public RottingFist() {
@ -286,7 +286,7 @@ public class Yog extends Mob {
EXP = 0; EXP = 0;
state = State.WANDERING; state = WANDERING;
} }
public BurningFist() { public BurningFist() {
@ -404,7 +404,7 @@ public class Yog extends Mob {
EXP = 0; EXP = 0;
state = State.HUNTING; state = HUNTING;
} }
@Override @Override

View File

@ -43,7 +43,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndQuest;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Random; import com.watabou.utils.Random;
public class Blacksmith extends Mob.NPC { public class Blacksmith extends NPC {
private static final String TXT_GOLD_1 = 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. " + "Hey human! Wanna be useful, eh? Take dis pickaxe and mine me some _dark gold ore_, _15 pieces_ should be enough. " +
@ -168,7 +168,7 @@ public class Blacksmith extends Mob.NPC {
return "Select 2 different items, not the same item twice!"; return "Select 2 different items, not the same item twice!";
} }
if (!item1.isSimilar( item2 )) { if (item1.getClass() != item2.getClass()) {
return "Select 2 items of the same type!"; return "Select 2 items of the same type!";
} }

View File

@ -19,6 +19,7 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs;
import java.util.HashSet; import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Challenges;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.StenchGas; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.StenchGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
@ -27,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Crab; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Crab;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Gnoll; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Gnoll;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Rat; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Rat;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClothArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat; import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.RatSkull; import com.shatteredpixel.shatteredpixeldungeon.items.quest.RatSkull;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand; import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
@ -61,7 +63,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndSadGhost;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Random; import com.watabou.utils.Random;
public class Ghost extends Mob.NPC { public class Ghost extends NPC {
{ {
name = "sad ghost"; name = "sad ghost";
@ -69,7 +71,7 @@ public class Ghost extends Mob.NPC {
flying = true; flying = true;
state = State.WANDERING; state = WANDERING;
} }
private static final String TXT_RAT1 = private static final String TXT_RAT1 =
@ -195,7 +197,6 @@ public class Ghost extends Mob.NPC {
txt_quest = TXT_CRAB1; break; txt_quest = TXT_CRAB1; break;
} }
questBoss.state = Mob.State.WANDERING;
questBoss.pos = Dungeon.level.randomRespawnCell(); questBoss.pos = Dungeon.level.randomRespawnCell();
if (questBoss.pos != -1) { if (questBoss.pos != -1) {
@ -353,6 +354,10 @@ public class Ghost extends Mob.NPC {
} }
} }
//TODO this is silly, why trap the player with bad armour? Just remove the button from the window.
if (Dungeon.isChallenged( Challenges.NO_ARMOR ))
armor = (Armor)new ClothArmor().degrade();
weapon.identify(); weapon.identify();
armor.identify(); armor.identify();
} }
@ -386,6 +391,8 @@ public class Ghost extends Mob.NPC {
defenseSkill = 4; defenseSkill = 4;
EXP = 4; EXP = 4;
state = WANDERING;
} }
@Override @Override
@ -445,6 +452,8 @@ public class Ghost extends Mob.NPC {
EXP = 5; EXP = 5;
state = WANDERING;
loot = Generator.random(CurareDart.class); loot = Generator.random(CurareDart.class);
lootChance = 1f; lootChance = 1f;
} }
@ -489,7 +498,7 @@ public class Ghost extends Mob.NPC {
@Override @Override
protected boolean getCloser( int target ) { protected boolean getCloser( int target ) {
combo = 0; //if he's moving, he isn't attacking, reset combo. combo = 0; //if he's moving, he isn't attacking, reset combo.
if (state == State.HUNTING) { if (state == HUNTING) {
return enemySeen && getFurther( target ); return enemySeen && getFurther( target );
} else { } else {
return super.getCloser( target ); return super.getCloser( target );
@ -573,6 +582,7 @@ public class Ghost extends Mob.NPC {
EXP = 6; EXP = 6;
state = WANDERING;
} }
private boolean moving = true; private boolean moving = true;

View File

@ -38,7 +38,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndQuest;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Random; import com.watabou.utils.Random;
public class Imp extends Mob.NPC { public class Imp extends NPC {
{ {
name = "ambitious imp"; name = "ambitious imp";

View File

@ -21,6 +21,8 @@ import java.util.HashSet;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; 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.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
@ -29,13 +31,13 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.MirrorSprite;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Random; import com.watabou.utils.Random;
public class MirrorImage extends Mob.NPC { public class MirrorImage extends NPC {
{ {
name = "mirror image"; name = "mirror image";
spriteClass = MirrorSprite.class; spriteClass = MirrorSprite.class;
state = State.HUNTING; state = HUNTING;
enemy = DUMMY; enemy = DUMMY;
} }
@ -135,4 +137,15 @@ public class MirrorImage extends Mob.NPC {
Dungeon.hero.spend( 1 / Dungeon.hero.speed() ); Dungeon.hero.spend( 1 / Dungeon.hero.speed() );
Dungeon.hero.busy(); Dungeon.hero.busy();
} }
private static final HashSet<Class<?>> IMMUNITIES = new HashSet<Class<?>>();
static {
IMMUNITIES.add( ToxicGas.class );
IMMUNITIES.add( Burning.class );
}
@Override
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
} }

View File

@ -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.npcs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.watabou.utils.Random;
public abstract class NPC extends Mob {
{
HP = HT = 1;
EXP = 0;
hostile = false;
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();
}

View File

@ -24,13 +24,13 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.sprites.RatKingSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.RatKingSprite;
public class RatKing extends Mob.NPC { public class RatKing extends NPC {
{ {
name = "rat king"; name = "rat king";
spriteClass = RatKingSprite.class; spriteClass = RatKingSprite.class;
state = State.SLEEPING; state = SLEEPEING;
} }
@Override @Override
@ -64,10 +64,10 @@ public class RatKing extends Mob.NPC {
@Override @Override
public void interact() { public void interact() {
sprite.turnTo( pos, Dungeon.hero.pos ); sprite.turnTo( pos, Dungeon.hero.pos );
if (state == State.SLEEPING) { if (state == SLEEPEING) {
notice(); notice();
yell( "I'm not sleeping!" ); yell( "I'm not sleeping!" );
state = State.WANDERING; state = WANDERING;
} else { } else {
yell( "What is it? I have no time for this nonsense. My kingdom won't rule itself!" ); yell( "What is it? I have no time for this nonsense. My kingdom won't rule itself!" );
} }

View File

@ -29,7 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.ShopkeeperSprite;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag; import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndTradeItem; import com.shatteredpixel.shatteredpixeldungeon.windows.WndTradeItem;
public class Shopkeeper extends Mob.NPC { public class Shopkeeper extends NPC {
public static final String TXT_THIEF = "Thief, Thief!"; public static final String TXT_THIEF = "Thief, Thief!";

View File

@ -63,7 +63,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndWandmaker;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Random; import com.watabou.utils.Random;
public class Wandmaker extends Mob.NPC { public class Wandmaker extends NPC {
{ {
name = "old wandmaker"; name = "old wandmaker";