v0.8.0: finished up mimic implementation, reduced their accuracy a bit

This commit is contained in:
Evan Debenham 2019-12-11 14:50:02 -05:00
parent faaa8530a3
commit 4e7aca628e
7 changed files with 162 additions and 14 deletions

View File

@ -24,22 +24,37 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
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.Corruption;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Haste;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Honeypot;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.Artifact;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.MimicSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
import java.util.ArrayList;
public class CrystalMimic extends Mimic {
{
spriteClass = MimicSprite.Crystal.class;
FLEEING = new Fleeing();
}
@Override
@ -54,7 +69,6 @@ public class CrystalMimic extends Mimic {
@Override
public String description() {
if (alignment == Alignment.NEUTRAL){
//TODO variable based on contents
for (Item i : items){
if (i instanceof Artifact){
return Messages.get(Heap.class, "crystal_chest_desc", Messages.get(Heap.class, "artifact"));
@ -70,8 +84,27 @@ public class CrystalMimic extends Mimic {
}
}
//does not deal bonus damage, steals instead. See attackProc
@Override
public int damageRoll() {
if (alignment == Alignment.NEUTRAL) {
alignment = Alignment.ENEMY;
int dmg = super.damageRoll();
alignment = Alignment.NEUTRAL;
return dmg;
} else {
return super.damageRoll();
}
}
public void stopHiding(){
state = HUNTING;
state = FLEEING;
//haste for 2 turns if attacking
if (alignment == Alignment.NEUTRAL){
Buff.affect(this, Haste.class, 2f);
} else {
Buff.affect(this, Haste.class, 1f);
}
if (Dungeon.level.heroFOV[pos] && Actor.chars().contains(this)) {
enemy = Dungeon.hero;
target = Dungeon.hero.pos;
@ -82,6 +115,82 @@ public class CrystalMimic extends Mimic {
}
}
//TODO different AI
@Override
public int attackProc(Char enemy, int damage) {
if (alignment == Alignment.NEUTRAL && enemy == Dungeon.hero){
steal( Dungeon.hero );
} else {
ArrayList<Integer> candidates = new ArrayList<>();
for (int i : PathFinder.NEIGHBOURS8){
if (Dungeon.level.passable[pos+i] && Actor.findChar(pos+i) == null){
candidates.add(pos + i);
}
}
if (!candidates.isEmpty()){
ScrollOfTeleportation.appear(enemy, Random.element(candidates));
}
if (alignment == Alignment.ENEMY) state = FLEEING;
}
return super.attackProc(enemy, damage);
}
protected void steal( Hero hero ) {
int tries = 10;
Item item;
do {
item = hero.belongings.randomUnequipped();
} while (tries-- > 0 && (item == null || item.unique || item.level() > 0));
if (item != null && !item.unique && item.level() < 1 ) {
GLog.w( Messages.get(this, "ate", item.name()) );
if (!item.stackable) {
Dungeon.quickslot.convertToPlaceholder(item);
}
item.updateQuickslot();
if (item instanceof Honeypot){
items.add(((Honeypot)item).shatter(this, this.pos));
item.detach( hero.belongings.backpack );
} else {
items.add(item.detach( hero.belongings.backpack ));
if ( item instanceof Honeypot.ShatteredPot)
((Honeypot.ShatteredPot)item).pickupPot(this);
}
}
}
@Override
protected void generatePrize() {
//Crystal mimic already contains a prize item. Just guarantee it isn't cursed.
for (Item i : items){
i.cursed = false;
i.cursedKnown = true;
}
}
private class Fleeing extends Mob.Fleeing{
@Override
protected void nowhereToRun() {
if (buff( Terror.class ) == null && buff( Corruption.class ) == null) {
if (enemySeen) {
sprite.showStatus(CharSprite.NEGATIVE, Messages.get(Mob.class, "rage"));
state = HUNTING;
} else {
GLog.n( Messages.get(CrystalMimic.class, "escaped"));
if (Dungeon.level.heroFOV[pos]) CellEmitter.get(pos).burst(Speck.factory(Speck.WOOL), 6);
destroy();
sprite.killAndErase();
}
} else {
super.nowhereToRun();
}
}
}
}

View File

@ -26,13 +26,17 @@ import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.EquipableItem;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.MimicSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
//TODO should enhance the reward from these a bit, as they are much harder
public class GoldenMimic extends Mimic {
{
@ -71,6 +75,24 @@ public class GoldenMimic extends Mimic {
@Override
public void adjustStats(int level) {
super.adjustStats((int)Math.ceil(level*1.5f));
super.adjustStats(Math.round(level*1.33f));
}
@Override
protected void generatePrize() {
super.generatePrize();
//all existing prize items are guaranteed uncursed
for (Item i : items){
if (i instanceof EquipableItem || i instanceof Wand){
i.cursed = false;
i.cursedKnown = true;
if (i instanceof Weapon && ((Weapon) i).hasCurseEnchant()){
((Weapon) i).enchant(null);
}
if (i instanceof Armor && ((Armor) i).hasCurseGlyph()){
((Armor) i).inscribe(null);
}
}
}
}
}

View File

@ -161,7 +161,7 @@ public class Mimic extends Mob {
@Override
public int damageRoll() {
if (alignment == Alignment.NEUTRAL){
return Random.NormalIntRange( 2*level, 2 + 3*level);
return Random.NormalIntRange( 2 + 2*level, 3 + 3*level);
} else {
return Random.NormalIntRange( 1 + level, 2 + 2*level);
}
@ -182,7 +182,7 @@ public class Mimic extends Mob {
if (target != null && alignment == Alignment.NEUTRAL){
return INFINITE_ACCURACY;
} else {
return 9 + level;
return 6 + level;
}
}
@ -246,6 +246,12 @@ public class Mimic extends Mob {
m.pos = pos;
//generate an extra reward for killing the mimic
m.generatePrize();
return m;
}
protected void generatePrize(){
Item reward = null;
do {
switch (Random.Int(5)) {
@ -266,9 +272,7 @@ public class Mimic extends Mob {
break;
}
} while (reward == null || Challenges.isItemBlocked(reward));
m.items.add(reward);
return m;
items.add(reward);
}
{

View File

@ -300,7 +300,7 @@ public abstract class RegularLevel extends Level {
case 4:
type = Heap.Type.CHEST;
break;
case 5:
case 5: default:
if (Dungeon.depth > 1 && findMob(cell) == null){
mobs.add(Mimic.spawnAt(cell, toDrop));
continue;
@ -312,7 +312,7 @@ public abstract class RegularLevel extends Level {
if ((toDrop instanceof Artifact && Random.Int(2) == 0) ||
(toDrop.isUpgradable() && Random.Int(4 - toDrop.level()) == 0)){
if (Random.Int(5) == 0 && findMob(cell) == null){
if (Dungeon.depth > 1 && Random.Int(10) == 0 && findMob(cell) == null){
mobs.add(Mimic.spawnAt(cell, toDrop, GoldenMimic.class));
} else {
Heap dropped = drop(toDrop, cell);

View File

@ -41,6 +41,11 @@ import java.util.Arrays;
public class VaultRoom extends SpecialRoom {
//size is reduced slightly to remove rare AI issues with crystal mimics
@Override
public int maxHeight() { return 8; }
public int maxWidth() { return 8; }
public void paint( Level level ) {
Painter.fill( level, this, Terrain.WALL );

View File

@ -43,7 +43,7 @@ public class MimicSprite extends MobSprite {
TextureFilm frames = new TextureFilm( texture, 16, 16 );
hiding = new Animation( 1, true );
hiding.frames( frames, 0+c, 0+c, 0+c, 0+c, 0+c, 0+c, 1+c);
hiding.frames( frames, 0+c, 0+c, 0+c, 0+c, 0+c, 1+c);
idle = new Animation( 5, true );
idle.frames( frames, 2+c, 2+c, 2+c, 3+c, 3+c );

View File

@ -473,6 +473,11 @@ actors.mobs.causticslime.desc=This slime seems to have been tainted by the dark
actors.mobs.crab.name=sewer crab
actors.mobs.crab.desc=These huge crabs are at the top of the food chain in the sewers. They are extremely fast and their thick carapace can withstand heavy blows.
actors.mobs.crystalmimic.name=crystal mimic
actors.mobs.crystalmimic.ate=The mimic ate your %s!
actors.mobs.crystalmimic.escaped=The crystal mimic has escaped!
actors.mobs.crystalmimic.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.\n\nCrystal mimics are trickier than their regular cousins, and prefer to avoid conflict while stealing loot. They will attempt to sprint away once discovered, and have the ability to reposition enemies when they attack.
actors.mobs.demonspawner.name=demon spawner
actors.mobs.demonspawner.desc=This twisting amalgam of dwarven flesh is responsible for creating ripper demons from dwarven body parts. Clearly the demons see no issue with using every resource they can against their enemies.\n\nWhile visually terrifying, demon spawners have no means of moving or directly defending themselves. Their considerable mass makes them hard to kill quickly however, and they will spawn ripper demons more quickly when they are under threat.
@ -515,6 +520,9 @@ actors.mobs.gnoll.desc=Gnolls are hyena-like humanoids. They dwell in sewers and
actors.mobs.gnolltrickster.name=gnoll trickster
actors.mobs.gnolltrickster.desc=A strange looking creature, even by gnoll standards. It hunches forward with a wicked grin, almost cradling the satchel hanging over its shoulder. Its eyes are wide with a strange mix of fear and excitement.\n\nThere is a large collection of poorly made darts in its satchel, they all seem to be tipped with various harmful substances.
actors.mobs.goldenmimic.name=golden mimic
actors.mobs.goldenmimic.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.\n\nGolden mimics are tougher mimics which try to attract the strongest adventurers. They have the best loot, but are also much stronger than regular mimics.
actors.mobs.golem.name=golem
actors.mobs.golem.def_verb=blocked
actors.mobs.golem.desc=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.
@ -554,7 +562,7 @@ actors.mobs.king$undead.desc=These undead dwarves, risen by the will of the King
actors.mobs.mimic.name=mimic
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.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.\n\nMimics have a nasty bite, but often hold more treasure than a regular chest.
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.