v0.8.0: implemented basic 1st phase of DK fight
This commit is contained in:
parent
e611f1a6ea
commit
82f87d9c7b
|
@ -22,25 +22,325 @@
|
|||
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.LifeLink;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.NewCityBossLevel;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.KingSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.BossHealthBar;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.noosa.particles.Emitter;
|
||||
import com.watabou.utils.Bundle;
|
||||
import com.watabou.utils.PathFinder;
|
||||
import com.watabou.utils.Random;
|
||||
import com.watabou.utils.Reflection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
//TODO currently just regular DK but with no summoning ability
|
||||
public class DwarfKing extends Mob {
|
||||
|
||||
//TODO decide on final stats, implement later phases
|
||||
{
|
||||
spriteClass = KingSprite.class;
|
||||
|
||||
HP = HT = 1;
|
||||
HP = HT = 300;
|
||||
EXP = 40;
|
||||
defenseSkill = 25;
|
||||
|
||||
properties.add(Property.BOSS);
|
||||
properties.add(Property.UNDEAD);
|
||||
}
|
||||
|
||||
protected boolean canTryToSummon() {
|
||||
@Override
|
||||
public int damageRoll() {
|
||||
return Random.NormalIntRange( 25, 40 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int attackSkill( Char target ) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drRoll() {
|
||||
return Random.NormalIntRange(0, 14);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean act() {
|
||||
if (buffs(Summoning.class).size() < 1) {
|
||||
|
||||
if (enemy != null
|
||||
&& Dungeon.level.adjacent(pos, enemy.pos) && teleportSubject()){
|
||||
spend(TICK);
|
||||
return true;
|
||||
} else if (lifeLinkSubject()) {
|
||||
spend(TICK);
|
||||
return true;
|
||||
} else {
|
||||
summonSubject();
|
||||
}
|
||||
|
||||
}
|
||||
return super.act();
|
||||
}
|
||||
|
||||
private boolean summonSubject(){
|
||||
Summoning s = new Summoning();
|
||||
s.pos = ((NewCityBossLevel)Dungeon.level).getSummoningPos();
|
||||
if (s.pos == -1) return false;
|
||||
switch (Random.Int(6)){
|
||||
default:
|
||||
s.summon = Ghoul.class;
|
||||
break;
|
||||
case 0:
|
||||
s.summon = Monk.class;
|
||||
break;
|
||||
case 1:
|
||||
s.summon = Warlock.class;
|
||||
break;
|
||||
}
|
||||
s.delay = 5;
|
||||
s.attachTo(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
private HashSet<Mob> getSubjects(){
|
||||
HashSet<Mob> subjects = new HashSet<>();
|
||||
for (Mob m : Dungeon.level.mobs){
|
||||
if (m.alignment == alignment && (m instanceof Ghoul || m instanceof Monk || m instanceof Warlock)){
|
||||
subjects.add(m);
|
||||
}
|
||||
}
|
||||
return subjects;
|
||||
}
|
||||
|
||||
private boolean lifeLinkSubject(){
|
||||
Mob furthest = null;
|
||||
|
||||
for (Mob m : getSubjects()){
|
||||
boolean alreadyLinked = false;
|
||||
for (LifeLink l : m.buffs(LifeLink.class)){
|
||||
if (l.object == id()) alreadyLinked = true;
|
||||
}
|
||||
if (!alreadyLinked) {
|
||||
if (furthest == null || Dungeon.level.distance(pos, furthest.pos) < Dungeon.level.distance(pos, m.pos)){
|
||||
furthest = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (furthest != null) {
|
||||
Buff.append(furthest, LifeLink.class, 100f).object = id();
|
||||
Buff.append(this, LifeLink.class, 100f).object = furthest.id();
|
||||
yell(Messages.get(this, "lifelink_" + Random.IntRange(1, 2)));
|
||||
sprite.parent.add(new Beam.HealthRay(sprite.destinationCenter(), furthest.sprite.destinationCenter()));
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean teleportSubject(){
|
||||
Mob furthest = null;
|
||||
|
||||
for (Mob m : getSubjects()){
|
||||
//TODO avoid warlocks?
|
||||
if (furthest == null || Dungeon.level.distance(pos, furthest.pos) < Dungeon.level.distance(pos, m.pos)){
|
||||
furthest = m;
|
||||
}
|
||||
}
|
||||
|
||||
if (furthest != null){
|
||||
|
||||
float bestDist;
|
||||
int bestPos = pos;
|
||||
|
||||
Ballistica trajectory = new Ballistica(enemy.pos, pos, Ballistica.STOP_TARGET);
|
||||
int targetCell = trajectory.path.get(trajectory.dist+1);
|
||||
//if the position opposite the direction of the hero is open, go there
|
||||
if (Actor.findChar(targetCell) == null && !Dungeon.level.solid[targetCell]){
|
||||
bestPos = targetCell;
|
||||
|
||||
//Otherwise go to the neighbour cell that's open and is furthest
|
||||
} else {
|
||||
bestDist = Dungeon.level.trueDistance(pos, enemy.pos);
|
||||
|
||||
for (int i : PathFinder.NEIGHBOURS8){
|
||||
if (Actor.findChar(pos+i) == null
|
||||
&& !Dungeon.level.solid[pos+1]
|
||||
&& Dungeon.level.trueDistance(pos+i, enemy.pos) > bestDist){
|
||||
bestPos = pos+i;
|
||||
bestDist = Dungeon.level.trueDistance(pos+i, enemy.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Actor.add(new Pushing(this, pos, bestPos));
|
||||
pos = bestPos;
|
||||
|
||||
//find closest cell that's adjacent to enemy, place subject there
|
||||
bestDist = Dungeon.level.trueDistance(enemy.pos, pos);
|
||||
bestPos = enemy.pos;
|
||||
for (int i : PathFinder.NEIGHBOURS8){
|
||||
if (Actor.findChar(enemy.pos+i) == null
|
||||
&& !Dungeon.level.solid[enemy.pos+1]
|
||||
&& Dungeon.level.trueDistance(enemy.pos+i, pos) < bestDist){
|
||||
bestPos = enemy.pos+i;
|
||||
bestDist = Dungeon.level.trueDistance(enemy.pos+i, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (bestPos != enemy.pos) ScrollOfTeleportation.appear(furthest, bestPos);
|
||||
yell(Messages.get(this, "teleport_" + Random.IntRange(1, 2)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notice() {
|
||||
super.notice();
|
||||
if (!BossHealthBar.isAssigned()) {
|
||||
BossHealthBar.assignBoss(this);
|
||||
yell(Messages.get(this, "notice"));
|
||||
for (Char ch : Actor.chars()){
|
||||
if (ch instanceof DriedRose.GhostHero){
|
||||
GLog.n("\n");
|
||||
((DriedRose.GhostHero) ch).sayBoss();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(Object cause) {
|
||||
super.die(cause);
|
||||
Dungeon.level.unseal();
|
||||
|
||||
for (Mob m : Dungeon.level.mobs.toArray(new Mob[0])){
|
||||
if (m instanceof Ghoul || m instanceof Monk || m instanceof Warlock){
|
||||
m.die(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Summoning extends Buff {
|
||||
|
||||
private int delay;
|
||||
private int pos;
|
||||
private Class<?extends Mob> summon;
|
||||
|
||||
private Emitter particles;
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean act() {
|
||||
delay--;
|
||||
|
||||
if (delay <= 0){
|
||||
|
||||
if (summon == Warlock.class){
|
||||
particles.burst(ShadowParticle.CURSE, 10);
|
||||
Sample.INSTANCE.play(Assets.SND_CURSED);
|
||||
} else if (summon == Monk.class){
|
||||
particles.burst(ElmoParticle.FACTORY, 10);
|
||||
Sample.INSTANCE.play(Assets.SND_BURNING);
|
||||
} else {
|
||||
particles.burst(Speck.factory(Speck.BONE), 10);
|
||||
Sample.INSTANCE.play(Assets.SND_BONES);
|
||||
}
|
||||
particles = null;
|
||||
|
||||
if (Actor.findChar(pos) != null){
|
||||
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()){
|
||||
pos = Random.element(candidates);
|
||||
}
|
||||
}
|
||||
|
||||
if (Actor.findChar(pos) == null) {
|
||||
Mob m = Reflection.newInstance(summon);
|
||||
if (m instanceof Ghoul) {
|
||||
((Ghoul) m).setSolo();
|
||||
}
|
||||
m.pos = pos;
|
||||
m.maxLvl = -2;
|
||||
GameScene.add(m);
|
||||
m.state = m.HUNTING;
|
||||
} else {
|
||||
Char ch = Actor.findChar(pos);
|
||||
ch.damage(Random.NormalIntRange(20, 40), summon);
|
||||
}
|
||||
|
||||
detach();
|
||||
}
|
||||
|
||||
spend(TICK);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fx(boolean on) {
|
||||
if (on && particles == null) {
|
||||
particles = CellEmitter.get(pos);
|
||||
|
||||
if (summon == Warlock.class){
|
||||
particles.pour(ShadowParticle.UP, 0.1f);
|
||||
} else if (summon == Monk.class){
|
||||
particles.pour(ElmoParticle.FACTORY, 0.1f);
|
||||
} else {
|
||||
particles.pour(Speck.factory(Speck.RATTLE), 0.1f);
|
||||
}
|
||||
|
||||
} else if (!on && particles != null) {
|
||||
particles.on = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DELAY = "delay";
|
||||
private static final String POS = "pos";
|
||||
private static final String SUMMON = "summon";
|
||||
|
||||
@Override
|
||||
public void storeInBundle(Bundle bundle) {
|
||||
super.storeInBundle(bundle);
|
||||
bundle.put(DELAY, delay);
|
||||
bundle.put(POS, pos);
|
||||
bundle.put(SUMMON, summon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
delay = bundle.getInt(DELAY);
|
||||
pos = bundle.getInt(POS);
|
||||
summon = bundle.getClass(SUMMON);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -95,6 +95,10 @@ public class Ghoul extends Mob {
|
|||
partnerID = bundle.getInt( PARTNER_ID );
|
||||
timesDowned = bundle.getInt( TIMES_DOWNED );
|
||||
}
|
||||
|
||||
public void setSolo(){
|
||||
partnerID = -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean act() {
|
||||
|
|
|
@ -46,6 +46,9 @@ import com.watabou.utils.Point;
|
|||
import com.watabou.utils.Random;
|
||||
import com.watabou.utils.Rect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class NewCityBossLevel extends Level {
|
||||
|
||||
{
|
||||
|
@ -53,6 +56,9 @@ public class NewCityBossLevel extends Level {
|
|||
color2 = 0xf2f2f2;
|
||||
}
|
||||
|
||||
private static int WIDTH = 15;
|
||||
private static int HEIGHT = 48;
|
||||
|
||||
private static final Rect entry = new Rect(1, 37, 14, 48);
|
||||
private static final Rect arena = new Rect(1, 25, 14, 38);
|
||||
private static final Rect end = new Rect(0, 0, 15, 22);
|
||||
|
@ -60,6 +66,16 @@ public class NewCityBossLevel extends Level {
|
|||
private static final int bottomDoor = 7 + (arena.bottom-1)*15;
|
||||
private static final int topDoor = 7 + arena.top*15;
|
||||
|
||||
private static final int[] pedestals = new int[4];
|
||||
|
||||
static{
|
||||
Point c = arena.center();
|
||||
pedestals[0] = c.x-3 + (c.y-3) * WIDTH;
|
||||
pedestals[1] = c.x+3 + (c.y-3) * WIDTH;
|
||||
pedestals[2] = c.x+3 + (c.y+3) * WIDTH;
|
||||
pedestals[3] = c.x-3 + (c.y+3) * WIDTH;
|
||||
}
|
||||
|
||||
private ImpShopRoom impShop;
|
||||
|
||||
@Override
|
||||
|
@ -90,7 +106,7 @@ public class NewCityBossLevel extends Level {
|
|||
@Override
|
||||
protected boolean build() {
|
||||
|
||||
setSize(15, 48);
|
||||
setSize(WIDTH, HEIGHT);
|
||||
|
||||
//entrance room
|
||||
Painter.fill(this, entry, Terrain.WALL);
|
||||
|
@ -124,10 +140,10 @@ public class NewCityBossLevel extends Level {
|
|||
Painter.set(this, c.x+3, c.y, Terrain.STATUE);
|
||||
Painter.set(this, c.x+4, c.y, Terrain.STATUE);
|
||||
|
||||
Painter.set(this, c.x-3, c.y-3, Terrain.PEDESTAL);
|
||||
Painter.set(this, c.x+3, c.y-3, Terrain.PEDESTAL);
|
||||
Painter.set(this, c.x+3, c.y+3, Terrain.PEDESTAL);
|
||||
Painter.set(this, c.x-3, c.y+3, Terrain.PEDESTAL);
|
||||
Painter.set(this, pedestals[0], Terrain.PEDESTAL);
|
||||
Painter.set(this, pedestals[1], Terrain.PEDESTAL);
|
||||
Painter.set(this, pedestals[2], Terrain.PEDESTAL);
|
||||
Painter.set(this, pedestals[3], Terrain.PEDESTAL);
|
||||
|
||||
Painter.set(this, c.x, arena.top, Terrain.LOCKED_DOOR);
|
||||
|
||||
|
@ -174,6 +190,37 @@ public class NewCityBossLevel extends Level {
|
|||
return true;
|
||||
}
|
||||
|
||||
//returns a random pedestal that doesn't already have a summon inbound on it
|
||||
public int getSummoningPos(){
|
||||
Mob king = getKing();
|
||||
HashSet<DwarfKing.Summoning> summons = king.buffs(DwarfKing.Summoning.class);
|
||||
ArrayList<Integer> positions = new ArrayList<>();
|
||||
for (int pedestal : pedestals) {
|
||||
boolean clear = true;
|
||||
for (DwarfKing.Summoning s : summons) {
|
||||
if (s.getPos() == pedestal) {
|
||||
clear = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clear) {
|
||||
positions.add(pedestal);
|
||||
}
|
||||
}
|
||||
if (positions.isEmpty()){
|
||||
return -1;
|
||||
} else {
|
||||
return Random.element(positions);
|
||||
}
|
||||
}
|
||||
|
||||
private Mob getKing(){
|
||||
for (Mob m : mobs){
|
||||
if (m instanceof DwarfKing) return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createMobs() {
|
||||
}
|
||||
|
|
|
@ -517,6 +517,19 @@ actors.mobs.newdm300.desc=The DM-300 is the largest and most powerful 'defense m
|
|||
actors.mobs.newdm300.desc_supercharged=DM-300 is currently charged full of electrical energy, In this form DM-300 cannot be damaged and moves at double speed! Additionally, its drill now spins fast enough for it to _tunnel through solid rock,_ though it moves much more slowly when doing this.\n\nAttacking DM-300 directly is pointless while it is supercharged, but _something in the area must be providing it with this energy,_ destroying that may weaken it.
|
||||
actors.mobs.newdm300$fallingrocks.desc=Loose rocks are tumbling down from the ceiling here, it looks like its about to collapse!
|
||||
|
||||
actors.mobs.dwarfking.name=Dwarf King
|
||||
actors.mobs.dwarfking.notice=How dare you! You have no idea what you're interfering with!
|
||||
actors.mobs.dwarfking.lifelink_1=I need of your essence, slave!
|
||||
actors.mobs.dwarfking.lifelink_2=Bleed for me, slave!
|
||||
actors.mobs.dwarfking.teleport_1=Deal with them, slave!
|
||||
actors.mobs.dwarfking.teleport_2=Keep them busy, slave!
|
||||
actors.mobs.dwarfking.wave_1=Enough! Kill them my slaves!
|
||||
actors.mobs.dwarfking.wave_2=More! Bleed for your king!
|
||||
actors.mobs.dwarfking.wave_3=Useless! KILL THEM NOW!
|
||||
actors.mobs.dwarfking.enraged=You cannot kill me %s, I AM IMMORTAL!
|
||||
actors.mobs.dwarfking.dieing=No! You can't do this... you have no idea what lies below...
|
||||
actors.mobs.dwarfking.dead=You've... Doomed us all...
|
||||
|
||||
actors.mobs.elemental$fire.name=fire elemental
|
||||
actors.mobs.elemental$fire.desc=Elementals are chaotic creatures that are often created when powerful occult magic isn't properly controlled. Elementals have minimal intelligence, and are usually associated with a particular type of magic.\n\nFire elementals are a common type of elemental which deals damage with fiery magic. They will set their target ablaze with melee attacks, and can occasionally shoot bolts of fire as well.
|
||||
actors.mobs.elemental$newbornfire.name=newborn fire elemental
|
||||
|
|
Loading…
Reference in New Issue
Block a user