From 95a905a33599d96a749d55c0b45738c3b1753b2f Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Sun, 15 Mar 2020 21:19:34 -0400 Subject: [PATCH] v0.8.0: added a leap ability to ripper demons in exchange for speed --- .../actors/mobs/RipperDemon.java | 170 +++++++++++++++++- .../effects/TargetedCell.java | 53 ++++++ 2 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/TargetedCell.java diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java index a4108f670..4724e6a0f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java @@ -21,8 +21,22 @@ 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.Bleeding; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing; +import com.shatteredpixel.shatteredpixeldungeon.effects.TargetedCell; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.sprites.RipperSprite; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Bundle; +import com.watabou.utils.Callback; +import com.watabou.utils.PathFinder; import com.watabou.utils.Random; public class RipperDemon extends Mob { @@ -36,7 +50,9 @@ public class RipperDemon extends Mob { EXP = 9; //for corrupting maxLvl = -2; - baseSpeed = 2f; + HUNTING = new Hunting(); + + baseSpeed = 1f; } @Override @@ -63,4 +79,156 @@ public class RipperDemon extends Mob { public int drRoll() { return Random.NormalIntRange(0, 4); } + + private static final String LAST_ENEMY_POS = "last_enemy_pos"; + private static final String LEAP_POS = "leap_pos"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(LAST_ENEMY_POS, lastEnemyPos); + bundle.put(LEAP_POS, leapPos); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + //pre beta-5.0 + if (bundle.contains(LAST_ENEMY_POS)) { + lastEnemyPos = bundle.getInt(LAST_ENEMY_POS); + leapPos = bundle.getInt(LEAP_POS); + } + } + + private int lastEnemyPos = -1; + + @Override + protected boolean act() { + AiState lastState = state; + boolean result = super.act(); + + //if state changed from wandering to hunting, we haven't acted yet, don't update. + if (!(lastState == WANDERING && state == HUNTING)) { + if (enemy != null) { + lastEnemyPos = enemy.pos; + } else { + lastEnemyPos = Dungeon.hero.pos; + } + } + + return result; + } + + private int leapPos = -1; + + public class Hunting extends Mob.Hunting { + + @Override + public boolean act( boolean enemyInFOV, boolean justAlerted ) { + + if (leapPos != -1){ + //do leap + sprite.visible = Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[leapPos]; + sprite.jump(pos, leapPos, new Callback() { + @Override + public void call() { + + Char ch = Actor.findChar(leapPos); + if (ch != null){ + if (alignment != ch.alignment){ + Buff.affect(ch, Bleeding.class).set(Math.max(damageRoll(), damageRoll())); + ch.sprite.flash(); + Sample.INSTANCE.play(Assets.SND_HIT); + } + //bounce to a random safe pos(if possible) + int bouncepos = leapPos; + for (int i : PathFinder.NEIGHBOURS8){ + if (Dungeon.level.trueDistance(pos, leapPos+i) < Dungeon.level.trueDistance(pos, bouncepos) + && Actor.findChar(leapPos+i) == null && Dungeon.level.passable[leapPos+i]){ + bouncepos = leapPos+i; + } + } + pos = bouncepos; + Actor.addDelayed(new Pushing(RipperDemon.this, leapPos, bouncepos), -1); + } else { + pos = leapPos; + } + + leapPos = -1; + Dungeon.level.occupyCell(RipperDemon.this); + next(); + } + }); + return false; + } + + enemySeen = enemyInFOV; + if (enemyInFOV && !isCharmedBy( enemy ) && canAttack( enemy )) { + + return doAttack( enemy ); + + } else { + + if (enemyInFOV) { + target = enemy.pos; + } else if (enemy == null) { + state = WANDERING; + target = Dungeon.level.randomDestination( RipperDemon.this ); + return true; + } + + if (Dungeon.level.distance(pos, enemy.pos) >= 3) { + + int targetPos = enemy.pos; + if (lastEnemyPos != enemy.pos){ + int closestIdx = 0; + for (int i = 1; i < PathFinder.CIRCLE8.length; i++){ + if (Dungeon.level.trueDistance(lastEnemyPos, enemy.pos+PathFinder.CIRCLE8[i]) + < Dungeon.level.trueDistance(lastEnemyPos, enemy.pos+PathFinder.CIRCLE8[closestIdx])){ + closestIdx = i; + } + } + targetPos = enemy.pos + PathFinder.CIRCLE8[(closestIdx+4)%8]; + } + + Ballistica b = new Ballistica(pos, targetPos, Ballistica.STOP_TARGET | Ballistica.STOP_TERRAIN); + //try aiming directly at hero if aiming near them doesn't work + if (b.collisionPos != targetPos && targetPos != enemy.pos){ + targetPos = enemy.pos; + b = new Ballistica(pos, targetPos, Ballistica.STOP_TARGET | Ballistica.STOP_TERRAIN); + } + if (b.collisionPos == targetPos){ + //get ready to leap + leapPos = targetPos; + spend(TICK); + if (Dungeon.level.heroFOV[leapPos]) { + sprite.parent.addToBack(new TargetedCell(leapPos, 0xFF0000)); + } + if (Dungeon.level.heroFOV[pos]){ + GLog.w(Messages.get(RipperDemon.this, "leap")); + } + return true; + } + } + + int oldPos = pos; + if (target != -1 && getCloser( target )) { + + spend( 1 / speed() ); + return moveSprite( oldPos, pos ); + + } else { + spend( TICK ); + if (!enemyInFOV) { + sprite.showLost(); + state = WANDERING; + target = Dungeon.level.randomDestination( RipperDemon.this ); + } + return true; + } + } + } + + } + } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/TargetedCell.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/TargetedCell.java new file mode 100644 index 000000000..6187190ea --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/TargetedCell.java @@ -0,0 +1,53 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2020 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.effects; + +import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap; +import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; + +public class TargetedCell extends Image { + + private float alpha; + + public TargetedCell( int pos, int color ) { + super(Icons.get(Icons.TARGET)); + hardlight(color); + + origin.set( width/2f ); + + point( DungeonTilemap.tileToWorld( pos ) ); + + alpha = 1f; + } + + @Override + public void update() { + if ((alpha -= Game.elapsed/2f) > 0) { + alpha( alpha ); + scale.set( alpha ); + } else { + killAndErase(); + } + } +}