v0.8.0: Fully implemented the new Yog-Dzewa fight!

This commit is contained in:
Evan Debenham 2020-03-19 21:16:47 -04:00
parent 60453b2a4e
commit b697ad1cae
6 changed files with 408 additions and 89 deletions

View File

@ -22,31 +22,32 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LockedFloor;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
import com.shatteredpixel.shatteredpixeldungeon.effects.TargetedCell;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PurpleParticle;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRetribution;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfPsionicBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.GrimTrap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.FistSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.LarvaSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.YogSprite;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
import com.shatteredpixel.shatteredpixeldungeon.ui.BossHealthBar;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Bundle;
@ -55,14 +56,15 @@ import com.watabou.utils.Random;
import com.watabou.utils.Reflection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
public class YogDzewa extends Mob {
{
spriteClass = YogSprite.class;
HP = HT = 600;
HP = HT = 1000;
EXP = 50;
@ -74,25 +76,212 @@ public class YogDzewa extends Mob {
}
private int phase = 0;
private Class[] toSummon = new Class[3];
private float abilityCooldown;
private static final int MIN_ABILITY_CD = 10;
private static final int MAX_ABILITY_CD = 15;
private float summonCooldown;
private static final int MIN_SUMMON_CD = 10;
private static final int MAX_SUMMON_CD = 15;
private ArrayList<Class> fistSummons = new ArrayList<>();
{
toSummon[0] = (Random.Int(2) == 0 ? YogFist.Burning.class : YogFist.Soiled.class);
toSummon[1] = (Random.Int(2) == 0 ? YogFist.Rotting.class : YogFist.Rusted.class);
toSummon[2] = (Random.Int(2) == 0 ? YogFist.Bright.class : YogFist.Dark.class);
Random.shuffle(toSummon);
fistSummons.add(Random.Int(2) == 0 ? YogFist.Burning.class : YogFist.Soiled.class);
fistSummons.add(Random.Int(2) == 0 ? YogFist.Rotting.class : YogFist.Rusted.class);
fistSummons.add(Random.Int(2) == 0 ? YogFist.Bright.class : YogFist.Dark.class);
Random.shuffle(fistSummons);
}
private static final int SUMMON_DECK_SIZE = 4;
private ArrayList<Class> regularSummons = new ArrayList<>();
{
for (int i = 0; i < SUMMON_DECK_SIZE; i++){
if (i >= Statistics.spawnersAlive){
regularSummons.add(Larva.class);
} else {
regularSummons.add(YogRipper.class);
}
}
Random.shuffle(regularSummons);
}
private ArrayList<Integer> targetedCells = new ArrayList<>();
@Override
protected boolean act() {
if (phase == 0 && Dungeon.hero.viewDistance >= Dungeon.level.distance(pos, Dungeon.hero.pos)){
Dungeon.observe();
if (Dungeon.level.heroFOV[pos]) {
notice();
}
}
if (phase == 4 && findFist() == null){
yell(Messages.get(this, "hope"));
summonCooldown = -15; //summon a burst of minions!
phase = 5;
}
if (phase == 0){
spend(TICK);
return true;
} else {
boolean terrainAffected = false;
HashSet<Char> affected = new HashSet<>();
for (int i : targetedCells){
Ballistica b = new Ballistica(pos, i, Ballistica.WONT_STOP);
//shoot beams
sprite.parent.add(new Beam.DeathRay(sprite.center(), DungeonTilemap.raisedTileCenterToWorld(b.collisionPos)));
for (int p : b.path){
Char ch = Actor.findChar(p);
if (ch != null && ch.alignment != alignment){
affected.add(ch);
}
if (Dungeon.level.flamable[p]){
Dungeon.level.destroy( p );
GameScene.updateMap( p );
terrainAffected = true;
}
}
}
if (terrainAffected){
Dungeon.observe();
}
for (Char ch : affected){
ch.damage(Random.NormalIntRange(20, 40), new Eye.DeathGaze());
if (Dungeon.level.heroFOV[pos]) {
ch.sprite.flash();
CellEmitter.center( pos ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) );
}
if (!ch.isAlive() && ch == Dungeon.hero) {
Dungeon.fail( getClass() );
GLog.n( Messages.get(Char.class, "kill", name()) );
}
}
targetedCells.clear();
if (abilityCooldown <= 0){
int beams = 1 + (HT - HP)/400;
HashSet<Integer> affectedCells = new HashSet<>();
for (int i = 0; i < beams; i++){
int targetPos = Dungeon.hero.pos;
if (i != 0){
do {
targetPos = Dungeon.hero.pos + PathFinder.NEIGHBOURS8[Random.Int(8)];
} while (Dungeon.level.trueDistance(pos, Dungeon.hero.pos)
> Dungeon.level.trueDistance(pos, targetPos));
}
targetedCells.add(targetPos);
Ballistica b = new Ballistica(pos, targetPos, Ballistica.WONT_STOP);
affectedCells.addAll(b.path);
}
//remove one beam if multiple shots would cause every cell next to the hero to be targeted
boolean allAdjTargeted = true;
for (int i : PathFinder.NEIGHBOURS9){
if (!affectedCells.contains(Dungeon.hero.pos + i) && Dungeon.level.passable[Dungeon.hero.pos + i]){
allAdjTargeted = false;
break;
}
}
if (allAdjTargeted){
targetedCells.remove(targetedCells.size()-1);
}
for (int i : targetedCells){
Ballistica b = new Ballistica(pos, i, Ballistica.WONT_STOP);
for (int p : b.path){
sprite.parent.add(new TargetedCell(p, 0xFF0000));
affectedCells.add(p);
}
}
//wait extra time to let a crippled/rooted hero evade
if (Dungeon.hero.buff(Cripple.class) != null){
spend(TICK);
} else if (Dungeon.hero.buff(Roots.class) != null){
spend(Dungeon.hero.buff(Roots.class).cooldown());
}
Dungeon.hero.interrupt();
abilityCooldown += Random.NormalFloat(MIN_ABILITY_CD, MAX_ABILITY_CD);
abilityCooldown -= phase;
}
while (summonCooldown <= 0){
Class<?extends Mob> cls = regularSummons.remove(0);
Mob summon = Reflection.newInstance(cls);
regularSummons.add(cls);
int spawnPos = -1;
for (int i : PathFinder.NEIGHBOURS8){
if (Actor.findChar(pos+i) == null){
if (spawnPos == -1 || Dungeon.level.trueDistance(Dungeon.hero.pos, spawnPos) > Dungeon.level.trueDistance(Dungeon.hero.pos, pos+i)){
spawnPos = pos + i;
}
}
}
if (spawnPos != -1) {
summon.pos = spawnPos;
GameScene.add( summon );
Actor.addDelayed( new Pushing( summon, pos, summon.pos ), -1 );
summonCooldown += Random.NormalFloat(MIN_SUMMON_CD, MAX_SUMMON_CD);
summonCooldown -= phase;
if (findFist() != null){
summonCooldown += MIN_SUMMON_CD - phase;
}
}
}
}
if (summonCooldown > 0) summonCooldown--;
if (abilityCooldown > 0) abilityCooldown--;
//extra fast abilities and summons at the final 100 HP
if (phase == 5 && abilityCooldown > 2){
abilityCooldown = 2;
}
if (phase == 5 && summonCooldown > 3){
summonCooldown = 3;
}
spend(TICK);
return true;
}
@Override
public boolean isAlive() {
return super.isAlive() || phase != 5;
}
@Override
public void damage( int dmg, Object src ) {
if (findFist() != null){
if (phase == 0 || findFist() != null){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
return;
}
int preHP = HP;
super.damage( dmg, src );
int dmgTaken = preHP - HP;
abilityCooldown -= dmgTaken/10f;
summonCooldown -= dmgTaken/10f;
if (phase < 4 && HP <= HT - 300*phase){
HP = HT - 300*phase;
if (phase < 3 && HP <= HT * (3 - phase)/4f){
Dungeon.level.viewDistance--;
if (Dungeon.hero.buff(Light.class) == null){
Dungeon.hero.viewDistance = Dungeon.level.viewDistance;
@ -101,14 +290,16 @@ public class YogDzewa extends Mob {
GLog.n(Messages.get(this, "darkness"));
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
YogFist fist = (YogFist) Reflection.newInstance(toSummon[phase]);
YogFist fist = (YogFist) Reflection.newInstance(fistSummons.remove(0));
fist.pos = Dungeon.level.exit;
//TODO change based on what fist is summoned?
CellEmitter.get(Dungeon.level.exit-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit).burst(ShadowParticle.UP, 100);
CellEmitter.get(Dungeon.level.exit+1).burst(ShadowParticle.UP, 25);
if (abilityCooldown < 5) abilityCooldown = 5;
if (summonCooldown < 5) summonCooldown = 5;
int targetPos = Dungeon.level.exit + Dungeon.level.width();
if (Actor.findChar(targetPos) == null){
fist.pos = targetPos;
@ -118,7 +309,7 @@ public class YogDzewa extends Mob {
fist.pos = targetPos+1;
}
GameScene.add(fist, 1);
GameScene.add(fist, 3);
Actor.addDelayed( new Pushing( fist, Dungeon.level.exit, fist.pos ), -1 );
phase++;
}
@ -165,7 +356,6 @@ public class YogDzewa extends Mob {
@Override
public void notice() {
super.notice();
if (!BossHealthBar.isAssigned()) {
BossHealthBar.assignBoss(this);
yell(Messages.get(this, "notice"));
@ -175,27 +365,78 @@ public class YogDzewa extends Mob {
((DriedRose.GhostHero) ch).sayBoss();
}
}
if (phase == 0) {
phase = 1;
summonCooldown = Random.NormalFloat(MIN_SUMMON_CD, MAX_SUMMON_CD);
abilityCooldown = Random.NormalFloat(MIN_ABILITY_CD, MAX_ABILITY_CD);
}
}
}
@Override
public String description() {
String desc = super.description();
if (Statistics.spawnersAlive > 0){
desc += "\n\n" + Messages.get(this, "desc_spawners");
}
return desc;
}
{
immunities.add( Grim.class );
immunities.add( GrimTrap.class );
immunities.add( Terror.class );
immunities.add( Amok.class );
immunities.add( Charm.class );
immunities.add( Sleep.class );
immunities.add( Burning.class );
immunities.add( ToxicGas.class );
immunities.add( ScrollOfRetribution.class );
immunities.add( ScrollOfPsionicBlast.class );
immunities.add( Vertigo.class );
}
private static final String PHASE = "phase";
private static final String ABILITY_CD = "ability_cd";
private static final String SUMMON_CD = "summon_cd";
private static final String FIST_SUMMONS = "fist_summons";
private static final String REGULAR_SUMMONS = "regular_summons";
private static final String TARGETED_CELLS = "targeted_cells";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(PHASE, phase);
bundle.put(ABILITY_CD, abilityCooldown);
bundle.put(SUMMON_CD, summonCooldown);
bundle.put(FIST_SUMMONS, fistSummons.toArray(new Class[0]));
bundle.put(REGULAR_SUMMONS, regularSummons.toArray(new Class[0]));
int[] bundleArr = new int[targetedCells.size()];
for (int i = 0; i < targetedCells.size(); i++){
bundleArr[i] = targetedCells.get(i);
}
bundle.put(TARGETED_CELLS, bundleArr);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
BossHealthBar.assignBoss(this);
phase = bundle.getInt(PHASE);
if (phase != 0) BossHealthBar.assignBoss(this);
abilityCooldown = bundle.getFloat(ABILITY_CD);
summonCooldown = bundle.getFloat(SUMMON_CD);
fistSummons.clear();
Collections.addAll(fistSummons, bundle.getClassArray(FIST_SUMMONS));
regularSummons.clear();
Collections.addAll(regularSummons, bundle.getClassArray(REGULAR_SUMMONS));
for (int i : bundle.getIntArray(TARGETED_CELLS)){
targetedCells.add(i);
}
}
public static class Larva extends Mob {
@ -203,8 +444,9 @@ public class YogDzewa extends Mob {
{
spriteClass = LarvaSprite.class;
HP = HT = 25;
defenseSkill = 20;
HP = HT = 20;
defenseSkill = 12;
viewDistance = Light.DISTANCE;
EXP = 5;
maxLvl = -2;
@ -221,13 +463,20 @@ public class YogDzewa extends Mob {
@Override
public int damageRoll() {
return Random.NormalIntRange( 22, 30 );
return Random.NormalIntRange( 15, 25 );
}
@Override
public int drRoll() {
return Random.NormalIntRange(0, 8);
return Random.NormalIntRange(0, 4);
}
}
//used so death to yog's ripper demons have their own rankings description and are more aggro
public static class YogRipper extends RipperDemon {
{
state = HUNTING;
}
}
}

View File

@ -30,7 +30,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze;
@ -48,6 +47,7 @@ import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.FistSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Bundle;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
@ -55,13 +55,15 @@ public abstract class YogFist extends Mob {
{
HP = HT = 300;
defenseSkill = 25;
defenseSkill = 20;
viewDistance = Light.DISTANCE;
//for doomed resistance
EXP = 25;
maxLvl = -2;
state = WANDERING;
state = HUNTING;
properties.add(Property.MINIBOSS);
properties.add(Property.DEMONIC);
@ -71,7 +73,7 @@ public abstract class YogFist extends Mob {
protected boolean canRangedInMelee = true;
protected void incrementRangedCooldown(){
rangedCooldown += Random.NormalFloat(6, 9);
rangedCooldown += Random.NormalFloat(8, 12);
}
@Override
@ -89,6 +91,26 @@ public abstract class YogFist extends Mob {
}
}
boolean immuneWarned = false;
protected boolean isNearYog(){
int yogPos = Dungeon.level.exit + 3*Dungeon.level.width();
return Dungeon.level.distance(pos, yogPos) <= 4;
}
@Override
public void damage(int dmg, Object src) {
if (isNearYog()){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
if (!immuneWarned){
immuneWarned = true;
GLog.w(Messages.get(this, "immune_hint"));
}
return;
}
super.damage(dmg, src);
}
@Override
protected boolean doAttack( Char enemy ) {
@ -122,9 +144,8 @@ public abstract class YogFist extends Mob {
}
@Override
//TODO different for some fists perhaps?
public int damageRoll() {
return Random.NormalIntRange( 26, 32 );
return Random.NormalIntRange( 18, 36 );
}
@Override
@ -132,6 +153,25 @@ public abstract class YogFist extends Mob {
return Random.NormalIntRange(0, 15);
}
@Override
public String description() {
return Messages.get(YogFist.class, "desc") + "\n\n" + Messages.get(this, "desc");
}
public static final String RANGED_COOLDOWN = "ranged_cooldown";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(RANGED_COOLDOWN, rangedCooldown);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
rangedCooldown = bundle.getFloat(RANGED_COOLDOWN);
}
public static class Burning extends YogFist {
{
@ -151,13 +191,11 @@ public abstract class YogFist extends Mob {
CellEmitter.get( pos ).burst( Speck.factory( Speck.STEAM ), 10 );
}
for (int i = 0; i < 2; i++){
int cell = pos + PathFinder.NEIGHBOURS8[Random.Int(8)];
if (Dungeon.level.map[cell] == Terrain.WATER){
Level.set( cell, Terrain.EMPTY);
GameScene.updateMap( cell );
CellEmitter.get( cell ).burst( Speck.factory( Speck.STEAM ), 10 );
}
int cell = pos + PathFinder.NEIGHBOURS8[Random.Int(8)];
if (Dungeon.level.map[cell] == Terrain.WATER){
Level.set( cell, Terrain.EMPTY);
GameScene.updateMap( cell );
CellEmitter.get( cell ).burst( Speck.factory( Speck.STEAM ), 10 );
}
for (int i : PathFinder.NEIGHBOURS9) {
@ -174,27 +212,12 @@ public abstract class YogFist extends Mob {
protected void zap() {
spend( 1f );
if (hit( this, enemy, true )) {
int dmg = damageRoll()/2;
//TODO damage source logic
enemy.damage( dmg, this );
Buff.affect( enemy, com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning.class ).reignite( enemy );
if (!enemy.isAlive() && enemy == Dungeon.hero) {
Dungeon.fail( getClass() );
GLog.n( Messages.get(Char.class, "kill", name()) );
}
} else {
enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() );
}
if (Dungeon.level.map[enemy.pos] == Terrain.WATER){
Level.set( enemy.pos, Terrain.EMPTY);
GameScene.updateMap( enemy.pos );
CellEmitter.get( enemy.pos ).burst( Speck.factory( Speck.STEAM ), 10 );
} else {
Buff.affect( enemy, com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning.class ).reignite( enemy );
}
for (int i : PathFinder.NEIGHBOURS9){
@ -221,19 +244,17 @@ public abstract class YogFist extends Mob {
boolean result = super.act();
for (int i = 0; i < 2; i++){
int cell = pos + PathFinder.NEIGHBOURS9[Random.Int(9)];
if (Dungeon.level.map[cell] == Terrain.GRASS){
Level.set( cell, Terrain.FURROWED_GRASS);
GameScene.updateMap( cell );
CellEmitter.get( cell ).burst( LeafParticle.GENERAL, 10 );
}
int cell = pos + PathFinder.NEIGHBOURS9[Random.Int(9)];
if (Dungeon.level.map[cell] == Terrain.GRASS){
Level.set( cell, Terrain.FURROWED_GRASS);
GameScene.updateMap( cell );
CellEmitter.get( cell ).burst( LeafParticle.GENERAL, 10 );
}
Dungeon.observe();
for (int i : PathFinder.NEIGHBOURS9) {
int cell = pos + i;
cell = pos + i;
if (canSpreadGrass(cell)){
Level.set(pos+i, Terrain.GRASS);
GameScene.updateMap( pos + i );
@ -252,7 +273,7 @@ public abstract class YogFist extends Mob {
grassCells++;
}
}
if (grassCells > 0) dmg = Math.round(dmg * (9-grassCells)/9f);
if (grassCells > 0) dmg = Math.round(dmg * (6-grassCells)/6f);
super.damage(dmg, src);
}
@ -263,9 +284,6 @@ public abstract class YogFist extends Mob {
if (hit( this, enemy, true )) {
int dmg = damageRoll()/2;
//TODO damage source logic
enemy.damage( dmg, this );
Buff.affect( enemy, Roots.class, 3f );
if (!enemy.isAlive() && enemy == Dungeon.hero) {
@ -326,13 +344,21 @@ public abstract class YogFist extends Mob {
@Override
public void damage(int dmg, Object src) {
if (isNearYog()){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
if (!immuneWarned){
immuneWarned = true;
GLog.w(Messages.get(this, "immune_hint"));
}
return;
}
if (!(src instanceof Bleeding)){
Bleeding b = buff(Bleeding.class);
if (b == null){
b = new Bleeding();
}
b.announced = false;
b.set(dmg/3f);
b.set(dmg*.67f);
b.attachTo(this);
sprite.showStatus(CharSprite.WARNING, b.toString() + " " + (int)b.level());
} else{
@ -373,8 +399,21 @@ public abstract class YogFist extends Mob {
properties.add(Property.INORGANIC);
}
@Override
public int damageRoll() {
return Random.NormalIntRange( 20, 40 );
}
@Override
public void damage(int dmg, Object src) {
if (isNearYog()){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
if (!immuneWarned){
immuneWarned = true;
GLog.w(Messages.get(this, "immune_hint"));
}
return;
}
if (!(src instanceof Viscosity.DeferedDamage)){
Buff.affect(this, Viscosity.DeferedDamage.class).prolong(dmg);
sprite.showStatus( CharSprite.WARNING, Messages.get(Viscosity.class, "deferred", dmg) );
@ -386,7 +425,7 @@ public abstract class YogFist extends Mob {
@Override
protected void zap() {
spend( 1f );
Buff.affect(enemy, Cripple.class, 5f);
Buff.affect(enemy, Cripple.class, 4f);
}
}
@ -406,16 +445,17 @@ public abstract class YogFist extends Mob {
//ranged attack has no cooldown
}
//used so resistances can differentiate between melee and magical attacks
public static class LightBeam{}
@Override
protected void zap() {
spend( 1f );
if (hit( this, enemy, true )) {
int dmg = damageRoll()/2;
//TODO damage source logic
enemy.damage( dmg, this );
Buff.prolong( enemy, Blindness.class, 5f );
enemy.damage( damageRoll(), new LightBeam() );
Buff.prolong( enemy, Blindness.class, 4f );
if (!enemy.isAlive() && enemy == Dungeon.hero) {
Dungeon.fail( getClass() );
@ -433,9 +473,9 @@ public abstract class YogFist extends Mob {
public void damage(int dmg, Object src) {
int beforeHP = HP;
super.damage(dmg, src);
if (beforeHP > HT/2 && HP < HT/2){
if (isAlive() && beforeHP > HT/2 && HP < HT/2){
HP = HT/2;
Buff.prolong( enemy, Blindness.class, 50f );
Buff.prolong( enemy, Blindness.class, 30f );
int i;
do {
i = Random.Int(Dungeon.level.length());
@ -443,6 +483,7 @@ public abstract class YogFist extends Mob {
ScrollOfTeleportation.appear(this, i);
state = WANDERING;
GameScene.flash(0xFFFFFF);
GLog.w( Messages.get( this, "teleport" ));
} else if (!isAlive()){
Buff.prolong( enemy, Blindness.class, 50f );
GameScene.flash(0xFFFFFF);
@ -464,15 +505,16 @@ public abstract class YogFist extends Mob {
//ranged attack has no cooldown
}
//used so resistances can differentiate between melee and magical attacks
public static class DarkBolt{}
@Override
protected void zap() {
spend( 1f );
if (hit( this, enemy, true )) {
int dmg = damageRoll()/2;
//TODO damage source logic
enemy.damage( dmg, this );
enemy.damage( damageRoll(), new DarkBolt() );
Light l = enemy.buff(Light.class);
if (l != null){
@ -495,7 +537,7 @@ public abstract class YogFist extends Mob {
public void damage(int dmg, Object src) {
int beforeHP = HP;
super.damage(dmg, src);
if (beforeHP > HT/2 && HP < HT/2){
if (isAlive() && beforeHP > HT/2 && HP < HT/2){
HP = HT/2;
Light l = Dungeon.hero.buff(Light.class);
if (l != null){
@ -508,6 +550,7 @@ public abstract class YogFist extends Mob {
ScrollOfTeleportation.appear(this, i);
state = WANDERING;
GameScene.flash(0, false);
GLog.w( Messages.get( this, "teleport" ));
} else if (!isAlive()){
Light l = Dungeon.hero.buff(Light.class);
if (l != null){

View File

@ -32,6 +32,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.DM100;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Shaman;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Warlock;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Yog;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.YogFist;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlastWave;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfDisintegration;
@ -81,6 +82,8 @@ public class AntiMagic extends Armor.Glyph {
RESISTS.add( Warlock.DarkBolt.class );
RESISTS.add( Eye.DeathGaze.class );
RESISTS.add( Yog.BurningFist.DarkBolt.class );
RESISTS.add( YogFist.Bright.LightBeam.class );
RESISTS.add( YogFist.Dark.DarkBolt.class );
}
@Override

View File

@ -748,11 +748,35 @@ actors.mobs.yog$larva.desc=Yog-Dzewa is an Old God, a powerful entity from the r
actors.mobs.yogdzewa.name=Yog-Dzewa
actors.mobs.yogdzewa.notice=I. SEE. YOU.
actors.mobs.yogdzewa.immune=IMMUNE
actors.mobs.yogdzewa.darkness=The shadows pull in closer.
actors.mobs.yogdzewa.darkness=The darkness pulls in closer...
actors.mobs.yogdzewa.hope=YOUR. HOPE. IS. AN. ILLUSION!
actors.mobs.yogdzewa.defeated=...
actors.mobs.yogdzewa.rankings_desc=Devoured by Yog-Dzewa
actors.mobs.yogdzewa.desc=TODO
actors.mobs.yogdzewa.desc=Yog-Dzewa is an Old God; a great monstrosity which reaches into mortal worlds and devours them to fuel its endless growth. The dwarves must have attracted the attention of this cosmic horror while toying with dark magics, and have been trying to keep it contained ever since.\n\nYog-Dzewa originates from a dimension of raw magical chaos, and cannot bring its true form into this world. Instead it has created avatars and summoned demonic minions to do its bidding here.\n\nThis giant eye is the focal point of Yog-Dzewa's power in this world. While the eye itself is immobile, it is capable of shooting powerful beams of demonic magic and summoning a variety of minions to aid it.
actors.mobs.yogdzewa.desc_spawners=You can feel waves of demonic energy radiating to the eye from the floors above. Something you left alive up there must be funneling power back into Yog-Dzewa. _Yog will use this energy to summon more powerful minions!_
actors.mobs.yogdzewa$larva.name=god's larva
actors.mobs.yogdzewa$larva.rankings_desc=Devoured by Yog-Dzewa
actors.mobs.yogdzewa$larva.desc=These tiny spawn of Yog-Dzewa are simple minions that are easy to produce. While individually weak, they are summoned quickly and can become overwhelming in large numbers.
actors.mobs.yogdzewa$yogripper.rankings_desc=Devoured by Yog-Dzewa
actors.mobs.yogfist.immune=IMMUNE
actors.mobs.yogfist.immune_hint=The fist is immune while near to the eye!
actors.mobs.yogfist.rankings_desc=Devoured by Yog-Dzewa
actors.mobs.yogfist.desc=This fist is an aspect of Yog-Dzewa's power. Fists are linked to the power of Yog-Dzewa, and will be protected from all damage when they are close to the eye.
actors.mobs.yogfist$burning.name=burning fist
actors.mobs.yogfist$burning.desc=This fist is formed out of chaotic fiery magic. As it moves it will constantly set terrain around it ablaze. It is also capable of shooting blasts of fiery power, which spread flames where the impact.\n\nThe fist's ability to spread flames is suppressed when near water, but it's heat is so great that any water around it will evaporate after a short while!
actors.mobs.yogfist$soiled.name=soiled fist
actors.mobs.yogfist$soiled.desc=This fist is formed out of chaotic earthen magic. As it moves it will constantly cause vegetation to sprout around it. It can also shoot concentrated earthen blasts that will ensnare its target in rapidly growing vines and roots!\n\nThe fist derives power from this plant life, and will resist damage based on the amount of tall or furrowed vegetation near it!
actors.mobs.yogfist$rotting.name=rotting fist
actors.mobs.yogfist$rotting.desc=This fist is formed out of living corruption. It is a towering mass of decaying flesh that can spread caustic ooze in melee and toxic gas at range. Even worse, the fist's great mass causes it to bleed instead of taking damage directly.\n\nWater can be used to wash the ooze off, but the fist is also capable of adsorbing water to heal itself! Additionally, damaging the fist while it is bleeding will cause the bleed to refresh, but not stack!
actors.mobs.yogfist$rusted.name=rusted fist
actors.mobs.yogfist$rusted.desc=This fist is formed out of living metal. It is a towering mass of shifting metal that has powerful melee attacks, and can cripple its targets at range. Because of its tremendous mass, the fist will take any damage dealt to it over time, instead of immediately.\n\nThe fist is inflexible however, and cannot move into tight passageways. Additionally, while it make take damage slowly, the deferred damage will continue to increase if the fist is hit multiple times.
actors.mobs.yogfist$bright.name=bright fist
actors.mobs.yogfist$bright.teleport=The fist teleports away in a flash of light that seriously blinds you!
actors.mobs.yogfist$bright.desc=This fist is formed out of pure light energy. It is capable of shooting powerful rays of searing light every turn! These beams will temporarily blind you in addition to dealing heavy damage. The fist is also capable of briefly engulphing the arena in blinding light in order to escape!\n\nThe fist has no special abilities which it can use in melee range however.
actors.mobs.yogfist$dark.name=dark fist
actors.mobs.yogfist$dark.teleport=The fist teleports away in a blast of darkness that extinguishes your light source!
actors.mobs.yogfist$dark.desc=This fist is formed out of pure dark energy. It is capable of shooting powerful blasts of dark magic every turn! These blasts will weaken your light source in addition to dealing heavy damage. The fist is also capable of briefly engulfing the arena in darkness in order to escape!\n\nThe fist has no special abilities which it can use in melee range however.
actors.char.kill=%s killed you...

View File

@ -1585,7 +1585,7 @@ items.weapon.weapon$enchantment.enchant=enchantment
items.amulet.name=amulet of yendor
items.amulet.ac_end=END THE GAME
items.amulet.rankings_desc=Obtained the Amulet of Yendor
items.amulet.desc=The Amulet of Yendor is the most powerful known artifact of unknown origin. It is said that the amulet is able to fulfil any wish if its owner's will-power is strong enough to "persuade" it to do it.
items.amulet.desc=The Amulet of Yendor is the most powerful artifact known to human or dwarf kind. Its shimmering crystals contain incredible and wondrous power.\n\nThe origins of the amulet are unknown. History says that the King of Dwarves boasted that he had discovered the artifact shortly before the Dwarven civilization cut off all contact. How did he find it? How did Yog-Dzewa take it from him? Questions for another time perhaps, all that matters now is the amulet is yours!
items.ankh.name=ankh
items.ankh.ac_bless=BLESS

View File

@ -8,7 +8,7 @@ scenes.alchemyscene.energy=Alchemical Energy: %d
scenes.amuletscene.exit=Let's call it a day
scenes.amuletscene.stay=I'm not done yet
scenes.amuletscene.text=You finally hold it in your hands, the Amulet of Yendor. Using its power you can take over the world or bring peace and prosperity to people or whatever. Anyway, your life will change forever and this game will end here. Or you can stay a mere mortal a little longer.
scenes.amuletscene.text=You finally hold it in your hands, the Amulet of Yendor! With the power of this amulet nothing will be able to stand in your way! You have conquered the dungeon and succeeded in your quest!\n\nOr, perhaps you're not ready yet? You can also decide to just hold onto the amulet, and stay a mere mortal a little longer...
scenes.badgesscene.title=Your Badges