v2.2.0: more crystal enemy impl

This commit is contained in:
Evan Debenham
2023-09-20 16:59:26 -04:00
parent c03acdc30f
commit a4db73be1a
4 changed files with 187 additions and 15 deletions

View File

@@ -21,11 +21,17 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Healing;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CrystalGuardianSprite;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
@@ -33,11 +39,34 @@ import com.watabou.utils.Random;
public class CrystalGuardian extends Mob{
{
HP = HT = 1;
spriteClass = CrystalGuardianSprite.class;
HP = HT = 100;
defenseSkill = 14;
EXP = 10;
maxLvl = -2;
SLEEPING = new Sleeping();
state = SLEEPING;
properties.add(Property.INORGANIC);
properties.add(Property.MINIBOSS);
}
@Override
public int damageRoll() {
return Random.NormalIntRange( 10, 20 );
}
@Override
public int attackSkill( Char target ) {
return 20;
}
@Override
public int drRoll() {
return super.drRoll() + Random.NormalIntRange(0, 10);
}
@Override
@@ -45,6 +74,17 @@ public class CrystalGuardian extends Mob{
return true;
}
@Override
public boolean isAlive() {
if (HP <= 0){
HP = 1;
if (sprite != null) ((CrystalGuardianSprite)sprite).crumple();
spend(10f-cooldown());
Buff.affect(this, Healing.class).setHeal(100, 0, 10);
}
return super.isAlive();
}
public CrystalGuardian(){
super();
switch (Random.Int(3)){
@@ -77,24 +117,32 @@ public class CrystalGuardian extends Mob{
@Override
public void beckon(int cell) {
super.beckon(cell);
state = HUNTING;
}
//If we are still penned into our starting area, break out of it
PathFinder.buildDistanceMap(cell, Dungeon.level.passable);
if (PathFinder.distance[pos] == Integer.MAX_VALUE){
boolean[] passable = Dungeon.level.passable.clone();
@Override
public void move(int step, boolean travelling) {
super.move(step, travelling);
if (Dungeon.level.map[pos] == Terrain.MINE_CRYSTAL){
Level.set(pos, Terrain.EMPTY);
GameScene.updateMap(pos);
if (Dungeon.level.heroFOV[pos]){
Splash.at(pos, 0xFFFFFF, 5);
Sample.INSTANCE.play( Assets.Sounds.SHATTER );
}
}
}
@Override
public boolean[] modifyPassable(boolean[] passable) {
//TODO maybe base this on passable instead of hunting?
// want to prevent one guardian randomly waking another though
if (state == HUNTING){
for (int i = 0; i < Dungeon.level.length(); i++){
passable[i] = passable[i] || Dungeon.level.map[i] == Terrain.MINE_CRYSTAL;
}
PathFinder.Path p = PathFinder.find(pos, cell, passable);
if (p != null) {
for (int i : p) {
if (Dungeon.level.map[i] == Terrain.MINE_CRYSTAL) {
Level.set(i, Terrain.EMPTY);
GameScene.updateMap(i);
}
}
}
}
return passable;
}
protected class Sleeping extends Mob.Sleeping{

View File

@@ -21,17 +21,34 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CrystalWispSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
public class CrystalWisp extends Mob{
{
HP = HT = 1;
spriteClass = CrystalWispSprite.class;
HP = HT = 35;
defenseSkill = 16;
EXP = 7;
maxLvl = -2;
flying = true;
properties.add(Property.INORGANIC);
}
public CrystalWisp(){
@@ -57,6 +74,75 @@ public class CrystalWisp extends Mob{
return passable;
}
@Override
public int damageRoll() {
return Random.NormalIntRange( 3, 10 );
}
@Override
public int attackSkill( Char target ) {
return 16;
}
@Override
public int drRoll() {
return super.drRoll() + Random.NormalIntRange(0, 5);
}
@Override
protected boolean canAttack( Char enemy ) {
return super.canAttack(enemy)
|| new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT).collisionPos == enemy.pos;
}
protected boolean doAttack(Char enemy ) {
if (Dungeon.level.adjacent( pos, enemy.pos )
|| new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT).collisionPos != enemy.pos) {
return super.doAttack( enemy );
} else {
if (sprite != null && (sprite.visible || enemy.sprite.visible)) {
sprite.zap( enemy.pos );
return false;
} else {
zap();
return true;
}
}
}
//used so resistances can differentiate between melee and magical attacks
public static class CrystalBolt{}
private void zap() {
spend( 1f );
Invisibility.dispel(this);
Char enemy = this.enemy;
if (hit( this, enemy, true )) {
int dmg = Random.NormalIntRange( 2, 8 );
dmg = Math.round(dmg * AscensionChallenge.statModifier(this));
enemy.damage( dmg, new CrystalBolt() );
if (!enemy.isAlive() && enemy == Dungeon.hero) {
Badges.validateDeathFromEnemyMagic();
Dungeon.fail( this );
GLog.n( Messages.get(this, "bolt_kill") );
}
} else {
enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() );
}
}
public void onZapComplete() {
zap();
next();
}
public static final String SPRITE = "sprite";
@Override

View File

@@ -22,11 +22,15 @@
package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Healing;
import com.watabou.noosa.MovieClip;
import com.watabou.noosa.TextureFilm;
public abstract class CrystalGuardianSprite extends MobSprite {
private Animation crumple;
public CrystalGuardianSprite() {
super();
@@ -48,9 +52,23 @@ public abstract class CrystalGuardianSprite extends MobSprite {
die = new MovieClip.Animation( 5, false );
die.frames( frames, 11+c, 12+c, 13+c, 14+c, 15+c, 15+c );
crumple = die.clone();
play( idle );
}
public void crumple(){
play(crumple);
}
@Override
public void link(Char ch) {
super.link(ch);
if (ch.buff(Healing.class) != null && ch.cooldown() > 0){
crumple();
}
}
protected abstract int texOffset();
public static class Blue extends CrystalGuardianSprite {

View File

@@ -22,6 +22,9 @@
package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.CrystalWisp;
import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
import com.watabou.noosa.TextureFilm;
public abstract class CrystalWispSprite extends MobSprite {
@@ -52,6 +55,23 @@ public abstract class CrystalWispSprite extends MobSprite {
play( idle );
}
public void zap( int cell ) {
super.zap( cell );
((CrystalWisp)ch).onZapComplete();
parent.add( new Beam.LightRay(center(), DungeonTilemap.raisedTileCenterToWorld(cell)));
}
@Override
public void onComplete( Animation anim ) {
if (anim == zap) {
idle();
}
super.onComplete( anim );
}
protected abstract int texOffset();
public static class Blue extends CrystalWispSprite {