v2.3.0: implemented very rough early logic for geomancer fight stages
This commit is contained in:
@@ -1209,6 +1209,8 @@ actors.mobs.gnoll.name=gnoll scout
|
||||
actors.mobs.gnoll.desc=Gnolls are hyena-like humanoids. They dwell in sewers and dungeons, venturing up to raid the surface from time to time. Gnoll scouts are regular members of their pack, they are not as strong as brutes and not as intelligent as shamans.
|
||||
|
||||
actors.mobs.gnollgeomancer.name=gnoll geomancer
|
||||
actors.mobs.gnollgeomancer.warning=The geomancer begins to stir as you start picking at its rock armor. Be ready for a fight if you continue!
|
||||
actors.mobs.gnollgeomancer.alert=The geomancer awakens! The entire cave seems to shift around as it leaps away while laughing madly!
|
||||
actors.mobs.gnollgeomancer.desc=TODO, fight info
|
||||
actors.mobs.gnollgeomancer.desc_sleeping=This impressively tall gnoll shaman is surrounded by a layer of rock, and looks almost like a statue. Looking closely you can see the rock magically move in time with the senior gnoll's breathing. It can't be harmed while encased in rock like this, and it appears to be enjoying a literal dirt nap.\n\nYou can probably break through the layers of rock with your pickaxe, but _be sure you're ready for a fight when you do so._ The geomancer must be the source of the various earth-moving magic around here, and the organizer of all the gnoll activity. _Defeating the gnolls scattered around here before fighting it might be a good idea._
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SacrificialFire;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AdrenalineSurge;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AnkhInvulnerability;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ArtifactRecharge;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge;
|
||||
@@ -1692,7 +1691,7 @@ public class Hero extends Char {
|
||||
|
||||
} else if (fieldOfView[cell] && ch instanceof Mob) {
|
||||
|
||||
if (ch.alignment != Alignment.ENEMY && ch.buff(Amok.class) == null) {
|
||||
if (((Mob) ch).heroShouldInteract()) {
|
||||
curAction = new HeroAction.Interact( ch );
|
||||
} else {
|
||||
curAction = new HeroAction.Attack( ch );
|
||||
|
||||
@@ -296,7 +296,7 @@ public class CrystalSpire extends Mob {
|
||||
|
||||
@Override
|
||||
public boolean isInvulnerable(Class effect) {
|
||||
return effect != Pickaxe.class;
|
||||
return super.isInvulnerable(effect) || effect != Pickaxe.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -312,7 +312,6 @@ public class CrystalSpire extends Mob {
|
||||
final Pickaxe p = Dungeon.hero.belongings.getItem(Pickaxe.class);
|
||||
|
||||
if (p == null){
|
||||
//maybe a game log entry here?
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,15 +21,38 @@
|
||||
|
||||
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.Invisibility;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ShieldBuff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.quest.DarkGold;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.quest.Pickaxe;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.GnollGeomancerSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.utils.Callback;
|
||||
import com.watabou.utils.PathFinder;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GnollGeomancer extends Mob {
|
||||
|
||||
{
|
||||
//TODO
|
||||
HP = HT = 300;
|
||||
HP = HT = 100;
|
||||
spriteClass = GnollGeomancerSprite.class;
|
||||
|
||||
EXP = 20;
|
||||
@@ -43,6 +66,174 @@ public class GnollGeomancer extends Mob {
|
||||
properties.add(Property.BOSS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvulnerable(Class effect) {
|
||||
return super.isInvulnerable(effect) || (buff(RockArmor.class) != null && effect != Pickaxe.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean heroShouldInteract() {
|
||||
return super.heroShouldInteract() || buff(RockArmor.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getCloser(int target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getFurther(int target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int hits = 0;
|
||||
int phase = 0;
|
||||
|
||||
@Override
|
||||
public boolean interact(Char c) {
|
||||
if (c != Dungeon.hero || buff(RockArmor.class) == null) {
|
||||
return super.interact(c);
|
||||
} else {
|
||||
final Pickaxe p = Dungeon.hero.belongings.getItem(Pickaxe.class);
|
||||
|
||||
if (p == null){
|
||||
return true;
|
||||
}
|
||||
|
||||
Dungeon.hero.sprite.attack(pos, new Callback() {
|
||||
@Override
|
||||
public void call() {
|
||||
//does its own special damage calculation that's only influenced by pickaxe level and augment
|
||||
//we pretend the geomancer is the owner here so that properties like hero str or or other equipment do not factor in
|
||||
int dmg = p.damageRoll(GnollGeomancer.this);
|
||||
|
||||
boolean wasSleeping = state == SLEEPING;
|
||||
|
||||
//ensure we don't do enough damage to break the barrier at the start
|
||||
if (wasSleeping) dmg = Math.min(dmg, 15);
|
||||
|
||||
dmg = Math.min(dmg, buff(RockArmor.class).shielding());
|
||||
|
||||
damage(dmg, p);
|
||||
sprite.bloodBurstA(Dungeon.hero.sprite.center(), dmg);
|
||||
sprite.flash();
|
||||
|
||||
hits++;
|
||||
if (hits == 1){
|
||||
GLog.n( Messages.get(GnollGeomancer.this, "warning"));
|
||||
} if (hits == 3){
|
||||
GLog.n( Messages.get(GnollGeomancer.this, "alert"));
|
||||
wasSleeping = false;
|
||||
spend(TICK);
|
||||
sprite.idle();
|
||||
|
||||
//this is a start, but need a lot more fight logic
|
||||
int target;
|
||||
do {
|
||||
target = Random.Int(Dungeon.level.length());
|
||||
} while (!Dungeon.level.insideMap(target) || Dungeon.level.distance(pos, target) != 10);
|
||||
carveRock(target);
|
||||
}
|
||||
|
||||
if (wasSleeping) {
|
||||
state = SLEEPING;
|
||||
alerted = false;
|
||||
}
|
||||
|
||||
if (buff(RockArmor.class) == null){
|
||||
sprite.idle();
|
||||
}
|
||||
|
||||
Sample.INSTANCE.play(Assets.Sounds.MINE, 1f, Random.Float(0.85f, 1.15f));
|
||||
Invisibility.dispel(Dungeon.hero);
|
||||
Dungeon.hero.spendAndNext(p.delayFactor(GnollGeomancer.this));
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(int dmg, Object src) {
|
||||
int hpBracket = HT / 3;
|
||||
|
||||
int beforeHitHP = HP;
|
||||
super.damage(dmg, src);
|
||||
dmg = beforeHitHP - HP;
|
||||
|
||||
//geomancer cannot be hit through multiple brackets at a time
|
||||
if ((beforeHitHP/hpBracket - HP/hpBracket) >= 2){
|
||||
HP = hpBracket * ((beforeHitHP/hpBracket)-1) + 1;
|
||||
}
|
||||
|
||||
if (beforeHitHP / hpBracket != HP / hpBracket) {
|
||||
//this is a start, but need a lot more fight logic
|
||||
int target;
|
||||
do {
|
||||
target = Random.Int(Dungeon.level.length());
|
||||
} while (!Dungeon.level.insideMap(target) || Dungeon.level.distance(pos, target) != 10);
|
||||
carveRock(target);
|
||||
Buff.affect(this, RockArmor.class).setShield(50);
|
||||
}
|
||||
}
|
||||
|
||||
private void carveRock(int target){
|
||||
Ballistica path = new Ballistica(pos, target, Ballistica.STOP_TARGET);
|
||||
|
||||
ArrayList<Integer> cells = new ArrayList<>(path.subPath(0, path.dist));
|
||||
cells.addAll(spreadDiamondAOE(cells));
|
||||
cells.addAll(spreadDiamondAOE(cells));
|
||||
cells.addAll(spreadDiamondAOE(cells));
|
||||
|
||||
ArrayList<Integer> exteriorCells = spreadDiamondAOE(cells);
|
||||
|
||||
for (int i : cells){
|
||||
if (Dungeon.level.map[i] == Terrain.WALL_DECO){
|
||||
Dungeon.level.drop(new DarkGold(), i).sprite.drop();
|
||||
}
|
||||
if (Dungeon.level.solid[i]){
|
||||
//TODO boulders?
|
||||
Dungeon.level.map[i] = Terrain.EMPTY_DECO;
|
||||
}
|
||||
CellEmitter.get( i - Dungeon.level.width() ).start(Speck.factory(Speck.ROCK), 0.07f, 10);
|
||||
}
|
||||
for (int i : exteriorCells){
|
||||
if (!Dungeon.level.solid[i]
|
||||
&& Dungeon.level.map[i] != Terrain.EMPTY_SP
|
||||
&& Dungeon.level.traps.get(i) == null
|
||||
&& Dungeon.level.plants.get(i) == null
|
||||
&& Actor.findChar(i) == null){
|
||||
Dungeon.level.map[i] = Terrain.MINE_BOULDER;
|
||||
}
|
||||
}
|
||||
//we potentially update a lot of cells, so might as well just reset properties instead of incrementally updating
|
||||
Dungeon.level.buildFlagMaps();
|
||||
Dungeon.level.cleanWalls();
|
||||
GameScene.updateMap();
|
||||
GameScene.updateFog();
|
||||
Dungeon.observe();
|
||||
|
||||
PixelScene.shake(3, 0.7f);
|
||||
Sample.INSTANCE.play(Assets.Sounds.ROCKS);
|
||||
|
||||
int oldpos = pos;
|
||||
pos = target;
|
||||
Actor.add(new Pushing(this, oldpos, pos));
|
||||
}
|
||||
|
||||
private ArrayList<Integer> spreadDiamondAOE(ArrayList<Integer> currentCells){
|
||||
ArrayList<Integer> spreadCells = new ArrayList<>();
|
||||
for (int i : currentCells){
|
||||
for (int j : PathFinder.NEIGHBOURS4){
|
||||
if (Dungeon.level.insideMap(i+j) && !spreadCells.contains(i+j) && !currentCells.contains(i+j)){
|
||||
spreadCells.add(i+j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return spreadCells;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
if (state == SLEEPING){
|
||||
@@ -62,7 +253,9 @@ public class GnollGeomancer extends Mob {
|
||||
|
||||
@Override
|
||||
protected void awaken(boolean enemyInFOV) {
|
||||
//TODO . do nothing for now
|
||||
//do nothing, has special awakening rules
|
||||
}
|
||||
}
|
||||
|
||||
public static class RockArmor extends ShieldBuff { }
|
||||
}
|
||||
|
||||
@@ -723,6 +723,11 @@ public abstract class Mob extends Char {
|
||||
&& (!attacking || enemy.canSurpriseAttack());
|
||||
}
|
||||
|
||||
//whether the hero should interact with the mob (true) or attack it (false)
|
||||
public boolean heroShouldInteract(){
|
||||
return alignment != Alignment.ENEMY && buff(Amok.class) == null;
|
||||
}
|
||||
|
||||
public void aggro( Char ch ) {
|
||||
enemy = ch;
|
||||
if (state != PASSIVE){
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.quest;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.CrystalSpire;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GnollGeomancer;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
|
||||
@@ -122,6 +123,7 @@ public class MineGiantRoom extends CaveRoom {
|
||||
|
||||
GnollGeomancer g = new GnollGeomancer();
|
||||
g.pos = level.pointToCell(center);
|
||||
Buff.affect(g, GnollGeomancer.RockArmor.class).setShield(50);
|
||||
level.mobs.add(g);
|
||||
|
||||
} else if (Blacksmith.Quest.Type() == Blacksmith.Quest.FUNGI){
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
package com.shatteredpixel.shatteredpixeldungeon.sprites;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Assets;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GnollGeomancer;
|
||||
import com.watabou.noosa.TextureFilm;
|
||||
import com.watabou.utils.ColorMath;
|
||||
|
||||
public class GnollGeomancerSprite extends MobSprite {
|
||||
|
||||
@@ -61,8 +62,13 @@ public class GnollGeomancerSprite extends MobSprite {
|
||||
@Override
|
||||
public void idle() {
|
||||
super.idle();
|
||||
if (ch instanceof Mob && ((Mob) ch).state == ((Mob) ch).SLEEPING){
|
||||
if (ch != null && ch.buff(GnollGeomancer.RockArmor.class) != null){
|
||||
play( statue );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int blood() {
|
||||
return curAnim == statue ? ColorMath.random( 0x444444, 0x777766 ) : super.blood();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user