v0.8.0: reworked mimics:

- evasion reduced slightly
- now have armor equal to depth/2
- damage increased by ~50%
- no longer give exp (same as other floor-independent mobs)
- now are visually different from chests, can be preemptively attacked
- will inflict bonus damage to the hero if they try to open them.
This commit is contained in:
Evan Debenham 2019-12-08 18:02:47 -05:00
parent 13d17234ec
commit 0351ad6475
13 changed files with 193 additions and 109 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -31,17 +31,22 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold; import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRetribution; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRetribution;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfPsionicBlast; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.MimicSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.MimicSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.PathFinder; import com.watabou.utils.PathFinder;
import com.watabou.utils.Random; import com.watabou.utils.Random;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -53,6 +58,12 @@ public class Mimic extends Mob {
spriteClass = MimicSprite.class; spriteClass = MimicSprite.class;
properties.add(Property.DEMONIC); properties.add(Property.DEMONIC);
EXP = 0;
//mimics are neutral when hidden
alignment = Alignment.NEUTRAL;
state = PASSIVE;
} }
public ArrayList<Item> items; public ArrayList<Item> items;
@ -75,24 +86,111 @@ public class Mimic extends Mob {
} }
adjustStats( bundle.getInt( LEVEL ) ); adjustStats( bundle.getInt( LEVEL ) );
super.restoreFromBundle(bundle); super.restoreFromBundle(bundle);
if (state != PASSIVE && alignment == Alignment.NEUTRAL){
alignment = Alignment.ENEMY;
}
}
@Override
public String name() {
if (alignment == Alignment.NEUTRAL){
return Messages.get(Heap.class, "chest");
} else {
return super.name();
}
}
@Override
public String description() {
if (alignment == Alignment.NEUTRAL){
return Messages.get(Heap.class, "chest_desc");
} else {
return super.description();
}
}
@Override
public CharSprite sprite() {
MimicSprite sprite = (MimicSprite) super.sprite();
if (alignment == Alignment.NEUTRAL) sprite.hideMimic();
return sprite;
}
@Override
public boolean interact() {
if (alignment != Alignment.NEUTRAL){
return super.interact();
}
stopHiding();
doAttack(Dungeon.hero);
Dungeon.hero.busy();
Dungeon.hero.sprite.operate(pos);
return false;
}
@Override
public void onAttackComplete() {
super.onAttackComplete();
if (alignment == Alignment.NEUTRAL){
alignment = Alignment.ENEMY;
Dungeon.hero.spendAndNext(1f);
}
}
@Override
public void damage(int dmg, Object src) {
if (state == PASSIVE){
alignment = Alignment.ENEMY;
stopHiding();
}
super.damage(dmg, src);
}
public void stopHiding(){
state = HUNTING;
if (Dungeon.level.heroFOV[pos] && Actor.chars().contains(this)) {
enemy = Dungeon.hero;
target = Dungeon.hero.pos;
enemySeen = true;
GLog.w(Messages.get(this, "reveal") );
CellEmitter.get(pos).burst(Speck.factory(Speck.STAR), 10);
Sample.INSTANCE.play(Assets.SND_MIMIC);
}
} }
@Override @Override
public int damageRoll() { public int damageRoll() {
return Random.NormalIntRange( HT / 10, HT / 4 ); if (alignment == Alignment.NEUTRAL){
return Random.NormalIntRange( 2*level, 2 + 3*level);
} else {
return Random.NormalIntRange( 1 + level, 2 + 2*level);
}
}
@Override
public int drRoll() {
return Random.NormalIntRange(0, 1 + level/2);
}
@Override
public void beckon( int cell ) {
// Do nothing
} }
@Override @Override
public int attackSkill( Char target ) { public int attackSkill( Char target ) {
return 9 + level; if (target != null && alignment == Alignment.NEUTRAL){
return INFINITE_ACCURACY;
} else {
return 9 + level;
}
} }
public void adjustStats( int level ) { public void adjustStats( int level ) {
this.level = level; this.level = level;
HP = HT = (1 + level) * 6; HP = HT = (1 + level) * 6;
EXP = 2 + 2 * (level - 1) / 5; defenseSkill = 2 + level/2;
defenseSkill = attackSkill( null ) / 2;
enemySeen = true; enemySeen = true;
} }
@ -120,42 +218,16 @@ public class Mimic extends Mob {
return true; return true;
} }
public static Mimic spawnAt( int pos, Item item ){
return spawnAt( pos, Arrays.asList(item));
}
public static Mimic spawnAt( int pos, List<Item> items ) { public static Mimic spawnAt( int pos, List<Item> items ) {
if (Dungeon.level.pit[pos]) return null;
Char ch = Actor.findChar( pos );
if (ch != null) {
ArrayList<Integer> candidates = new ArrayList<>();
for (int n : PathFinder.NEIGHBOURS8) {
int cell = pos + n;
if ((Dungeon.level.passable[cell] || Dungeon.level.avoid[cell]) && Actor.findChar( cell ) == null) {
candidates.add( cell );
}
}
if (candidates.size() > 0) {
int newPos = Random.element( candidates );
Actor.addDelayed( new Pushing( ch, ch.pos, newPos ), -1 );
ch.pos = newPos;
Dungeon.level.occupyCell(ch );
} else {
return null;
}
}
Mimic m = new Mimic(); Mimic m = new Mimic();
m.items = new ArrayList<>( items ); m.items = new ArrayList<>( items );
m.adjustStats( Dungeon.depth ); m.adjustStats( Dungeon.depth );
m.pos = pos; m.pos = pos;
m.state = m.HUNTING;
GameScene.add( m, 1 );
m.sprite.turnTo( pos, Dungeon.hero.pos );
if (Dungeon.level.heroFOV[m.pos]) {
CellEmitter.get( pos ).burst( Speck.factory( Speck.STAR ), 10 );
Sample.INSTANCE.play( Assets.SND_MIMIC );
}
//generate an extra reward for killing the mimic //generate an extra reward for killing the mimic
Item reward = null; Item reward = null;

View File

@ -72,7 +72,7 @@ public class Heap implements Bundlable {
TOMB, TOMB,
SKELETON, SKELETON,
REMAINS, REMAINS,
MIMIC MIMIC //remains for pre-0.8.0 compatibility. There are converted to mimics on level load
} }
public Type type = Type.HEAP; public Type type = Type.HEAP;
@ -87,11 +87,7 @@ public class Heap implements Bundlable {
public void open( Hero hero ) { public void open( Hero hero ) {
switch (type) { switch (type) {
case MIMIC: case MIMIC:
if (Mimic.spawnAt(pos, items) != null) { type = Type.CHEST;
destroy();
} else {
type = Type.CHEST;
}
break; break;
case TOMB: case TOMB:
Wraith.spawnAround( hero.pos ); Wraith.spawnAround( hero.pos );
@ -207,15 +203,6 @@ public class Heap implements Bundlable {
public void burn() { public void burn() {
if (type == Type.MIMIC) {
Mimic m = Mimic.spawnAt( pos, items );
if (m != null) {
Buff.affect( m, Burning.class ).reignite( m );
m.sprite.emitter().burst( FlameParticle.FACTORY, 5 );
destroy();
}
}
if (type != Type.HEAP) { if (type != Type.HEAP) {
return; return;
} }
@ -285,7 +272,7 @@ public class Heap implements Bundlable {
for (Item item : items.toArray( new Item[0] )) { for (Item item : items.toArray( new Item[0] )) {
if (item instanceof Potion) { if (item instanceof Potion) {
items.remove( item ); items.remove(item);
((Potion) item).shatter(pos); ((Potion) item).shatter(pos);
} else if (item instanceof Bomb) { } else if (item instanceof Bomb) {
@ -313,14 +300,6 @@ public class Heap implements Bundlable {
public void freeze() { public void freeze() {
if (type == Type.MIMIC) {
Mimic m = Mimic.spawnAt( pos, items );
if (m != null) {
Buff.prolong( m, Frost.class, Frost.duration( m ) * Random.Float( 1.0f, 1.5f ) );
destroy();
}
}
if (type != Type.HEAP) { if (type != Type.HEAP) {
return; return;
} }

View File

@ -52,16 +52,6 @@ public class Noisemaker extends Bomb {
mob.beckon( cell ); mob.beckon( cell );
} }
for (Heap heap : Dungeon.level.heaps.valueList()) {
if (heap.type == Heap.Type.MIMIC) {
Mimic m = Mimic.spawnAt( heap.pos, heap.items );
if (m != null) {
m.beckon( cell );
heap.destroy();
}
}
}
} }
public static class Trigger extends Buff { public static class Trigger extends Buff {

View File

@ -51,16 +51,6 @@ public class ScrollOfRage extends Scroll {
} }
} }
for (Heap heap : Dungeon.level.heaps.valueList()) {
if (heap.type == Heap.Type.MIMIC) {
Mimic m = Mimic.spawnAt( heap.pos, heap.items );
if (m != null) {
m.beckon( curUser.pos );
heap.destroy();
}
}
}
GLog.w( Messages.get(this, "roar") ); GLog.w( Messages.get(this, "roar") );
setKnown(); setKnown();

View File

@ -370,8 +370,11 @@ public class CursedWand {
case 1: case 1:
cursedFX(user, bolt, new Callback() { cursedFX(user, bolt, new Callback() {
public void call() { public void call() {
//TODO make this a gold mimic instead of boosting stats artificially
Mimic mimic = Mimic.spawnAt(bolt.collisionPos, new ArrayList<Item>()); Mimic mimic = Mimic.spawnAt(bolt.collisionPos, new ArrayList<Item>());
if (mimic != null) { if (mimic != null) {
mimic.stopHiding();
mimic.alignment = Char.Alignment.ENEMY;
mimic.adjustStats(Dungeon.depth + 10); mimic.adjustStats(Dungeon.depth + 10);
mimic.HP = mimic.HT; mimic.HP = mimic.HT;
Item reward; Item reward;
@ -382,6 +385,7 @@ public class CursedWand {
Sample.INSTANCE.play(Assets.SND_MIMIC, 1, 1, 0.5f); Sample.INSTANCE.play(Assets.SND_MIMIC, 1, 1, 0.5f);
mimic.items.clear(); mimic.items.clear();
mimic.items.add(reward); mimic.items.add(reward);
GameScene.add(mimic);
} else { } else {
GLog.i(Messages.get(CursedWand.class, "nothing")); GLog.i(Messages.get(CursedWand.class, "nothing"));
} }

View File

@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bestiary; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bestiary;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Sheep; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Sheep;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlowParticle; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlowParticle;
@ -375,6 +376,14 @@ public abstract class Level implements Bundlable {
buildFlagMaps(); buildFlagMaps();
cleanWalls(); cleanWalls();
//compat with pre-0.8.0 saves
for (Heap h : heaps.valueList()){
if (h.type == Heap.Type.MIMIC){
heaps.remove(h.pos);
mobs.add(Mimic.spawnAt(h.pos, h.items));
}
}
} }
@Override @Override

View File

@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels;
import com.shatteredpixel.shatteredpixeldungeon.Bones; import com.shatteredpixel.shatteredpixeldungeon.Bones;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
@ -277,6 +278,16 @@ public abstract class RegularLevel extends Level {
int nItems = 3 + Random.chances(new float[]{6, 3, 1}); int nItems = 3 + Random.chances(new float[]{6, 3, 1});
for (int i=0; i < nItems; i++) { for (int i=0; i < nItems; i++) {
Item toDrop = Generator.random();
if (toDrop == null) continue;
int cell = randomDropCell();
if (map[cell] == Terrain.HIGH_GRASS || map[cell] == Terrain.FURROWED_GRASS) {
map[cell] = Terrain.GRASS;
losBlocking[cell] = false;
}
Heap.Type type = null; Heap.Type type = null;
switch (Random.Int( 20 )) { switch (Random.Int( 20 )) {
case 0: case 0:
@ -289,21 +300,17 @@ public abstract class RegularLevel extends Level {
type = Heap.Type.CHEST; type = Heap.Type.CHEST;
break; break;
case 5: case 5:
type = Dungeon.depth > 1 ? Heap.Type.MIMIC : Heap.Type.CHEST; if (Dungeon.depth > 1 && findMob(cell) == null){
mobs.add(Mimic.spawnAt(cell, toDrop));
continue;
}
type = Heap.Type.CHEST;
break; break;
default: default:
type = Heap.Type.HEAP; type = Heap.Type.HEAP;
} }
int cell = randomDropCell();
if (map[cell] == Terrain.HIGH_GRASS || map[cell] == Terrain.FURROWED_GRASS) {
map[cell] = Terrain.GRASS;
losBlocking[cell] = false;
}
Item toDrop = Generator.random();
if (toDrop == null) continue;
//TODO gold mimics
if ((toDrop instanceof Artifact && Random.Int(2) == 0) || if ((toDrop instanceof Artifact && Random.Int(2) == 0) ||
(toDrop.isUpgradable() && Random.Int(4 - toDrop.level()) == 0)){ (toDrop.isUpgradable() && Random.Int(4 - toDrop.level()) == 0)){
Heap dropped = drop( toDrop, cell ); Heap dropped = drop( toDrop, cell );

View File

@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special; package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold; import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.IronKey; import com.shatteredpixel.shatteredpixeldungeon.items.keys.IronKey;
@ -47,7 +48,11 @@ public class TreasuryRoom extends SpecialRoom {
do { do {
pos = level.pointToCell(random()); pos = level.pointToCell(random());
} while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null); } while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null);
level.drop( new Gold().random(), pos ).type = (Random.Int(20) == 0 && heapType == Heap.Type.CHEST ? Heap.Type.MIMIC : heapType); if (heapType == Heap.Type.CHEST && Random.Int(5 ) == 0){
level.mobs.add(Mimic.spawnAt(pos, new Gold().random()));
} else {
level.drop( new Gold().random(), pos ).type = heapType;
}
} }
if (heapType == Heap.Type.HEAP) { if (heapType == Heap.Type.HEAP) {

View File

@ -21,6 +21,7 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard; package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold; import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
@ -56,7 +57,7 @@ public class SuspiciousChestRoom extends EmptyRoom {
Painter.set(level, center, Terrain.PEDESTAL); Painter.set(level, center, Terrain.PEDESTAL);
if (Random.Int(3) == 0) { if (Random.Int(3) == 0) {
level.drop(i, center).type = Heap.Type.MIMIC; level.mobs.add(Mimic.spawnAt(center, i));
} else { } else {
level.drop(i, center).type = Heap.Type.CHEST; level.drop(i, center).type = Heap.Type.CHEST;
} }

View File

@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.traps;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Acidic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Acidic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Albino; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Albino;
@ -121,8 +122,10 @@ public class DistortionTrap extends Trap{
mob = new Piranha(); mob = new Piranha();
break; break;
case 2: case 2:
Mimic.spawnAt(point, new ArrayList<>()); mob = Mimic.spawnAt(point, new ArrayList<>());
continue; //mimics spawn themselves, no need to do more ((Mimic)mob).stopHiding();
mob.alignment = Char.Alignment.ENEMY;
break;
case 3: case 3:
mob = Statue.random(); mob = Statue.random();
break; break;

View File

@ -22,10 +22,13 @@
package com.shatteredpixel.shatteredpixeldungeon.sprites; package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.watabou.noosa.TextureFilm; import com.watabou.noosa.TextureFilm;
public class MimicSprite extends MobSprite { public class MimicSprite extends MobSprite {
private Animation hiding;
public MimicSprite() { public MimicSprite() {
super(); super();
@ -33,23 +36,43 @@ public class MimicSprite extends MobSprite {
TextureFilm frames = new TextureFilm( texture, 16, 16 ); TextureFilm frames = new TextureFilm( texture, 16, 16 );
hiding = new Animation( 1, true );
hiding.frames( frames, 0, 0, 0, 0, 0, 0, 1);
idle = new Animation( 5, true ); idle = new Animation( 5, true );
idle.frames( frames, 0, 0, 0, 1, 1 ); idle.frames( frames, 2, 2, 2, 3, 3 );
run = new Animation( 10, true ); run = new Animation( 10, true );
run.frames( frames, 0, 1, 2, 3, 3, 2, 1 ); run.frames( frames, 2, 3, 4, 5, 5, 4, 3 );
attack = new Animation( 10, false ); attack = new Animation( 10, false );
attack.frames( frames, 0, 4, 5, 6 ); attack.frames( frames, 2, 6, 7, 8 );
die = new Animation( 5, false ); die = new Animation( 5, false );
die.frames( frames, 7, 8, 9 ); die.frames( frames, 9, 10, 11 );
play( idle ); play( idle );
} }
@Override @Override
public int blood() { public void linkVisuals(Char ch) {
return 0xFFcb9700; super.linkVisuals(ch);
if (ch.alignment == Char.Alignment.NEUTRAL) {
hideMimic();
}
} }
public void hideMimic(){
play(hiding);
hideSleep();
}
@Override
public synchronized void showSleep() {
if (curAnim == hiding){
return;
}
super.showSleep();
}
} }

View File

@ -553,7 +553,8 @@ actors.mobs.king$undead.rankings_desc=Fell Before the King of Dwarves
actors.mobs.king$undead.desc=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. actors.mobs.king$undead.desc=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.
actors.mobs.mimic.name=mimic actors.mobs.mimic.name=mimic
actors.mobs.mimic.desc=Mimics are magical creatures which can take any shape they wish. In dungeons they almost always choose a shape of a treasure chest, because they know how to beckon an adventurer. actors.mobs.mimic.reveal=That chest is a mimic!
actors.mobs.mimic.desc=Mimics are magical creatures which can take any shape they wish. In dungeons they almost always choose a shape of a treasure chest, in order to lure in unsuspecting adventurers. Mimics have a nasty bite, but often hold more treasure than a regular chest.
actors.mobs.necromancer.name=necromancer actors.mobs.necromancer.name=necromancer
actors.mobs.necromancer.desc=These apprentice dark mages have flocked to the prison, as it is the perfect place to practise their evil craft.\n\nNecromancers will summon and empower skeletons to fight for them. Killing the necromancer will also kill the skeleton it summons. actors.mobs.necromancer.desc=These apprentice dark mages have flocked to the prison, as it is the perfect place to practise their evil craft.\n\nNecromancers will summon and empower skeletons to fight for them. Killing the necromancer will also kill the skeleton it summons.