v0.8.0: redesigned cave spinners

This commit is contained in:
Evan Debenham 2019-11-14 12:10:25 -05:00
parent 1bc5845e77
commit c68f27c774
6 changed files with 177 additions and 21 deletions

View File

@ -28,31 +28,38 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.WebParticle;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
public class Web extends Blob {
{
//acts before the hero, to ensure terrain is adjusted correctly
actPriority = HERO_PRIO+1;
}
@Override
protected void evolve() {
int cell;
Level l = Dungeon.level;
for (int i = area.left; i < area.right; i++){
for (int j = area.top; j < area.bottom; j++){
cell = i + j*Dungeon.level.width();
cell = i + j*l.width();
off[cell] = cur[cell] > 0 ? cur[cell] - 1 : 0;
if (off[cell] > 0) {
volume += off[cell];
Char ch = Actor.findChar( cell );
if (ch != null && !ch.isImmune(this.getClass())) {
Buff.prolong( ch, Roots.class, TICK );
}
l.solid[cell] = off[cell] > 0 || (Terrain.flags[l.map[cell]] & Terrain.SOLID) != 0;
}
}
}
//affects characters as they step on it. See Level.OccupyCell and Level.PressCell
public static void affectChar( Char ch ){
Buff.prolong( ch, Roots.class, 5f );
}
@Override
@ -62,6 +69,19 @@ public class Web extends Blob {
emitter.pour( WebParticle.FACTORY, 0.4f );
}
@Override
public void clear(int cell) {
super.clear(cell);
Level l = Dungeon.level;
l.solid[cell] = cur[cell] > 0 || (Terrain.flags[l.map[cell]] & Terrain.SOLID) != 0;
}
@Override
public void fullyClear() {
super.fullyClear();
Dungeon.level.buildFlagMaps();
}
@Override
public String tileDesc() {
return Messages.get(this, "desc");

View File

@ -21,6 +21,7 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Web;
@ -28,8 +29,10 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.SpinnerSprite;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
public class Spinner extends Mob {
@ -38,7 +41,7 @@ public class Spinner extends Mob {
spriteClass = SpinnerSprite.class;
HP = HT = 50;
defenseSkill = 14;
defenseSkill = 17;
EXP = 9;
maxLvl = 17;
@ -56,7 +59,7 @@ public class Spinner extends Mob {
@Override
public int attackSkill(Char target) {
return 20;
return 22;
}
@Override
@ -64,10 +67,24 @@ public class Spinner extends Mob {
return Random.NormalIntRange(0, 6);
}
private int webCoolDown = 0;
private int lastEnemyPos = -1;
@Override
protected boolean act() {
boolean result = super.act();
webCoolDown--;
if (shotWebVisually){
result = shotWebVisually = false;
} else {
if (enemy != null && enemySeen) {
lastEnemyPos = enemy.pos;
} else {
lastEnemyPos = -1;
}
}
if (state == FLEEING && buff( Terror.class ) == null &&
enemy != null && enemySeen && enemy.buff( Poison.class ) == null) {
state = HUNTING;
@ -80,21 +97,89 @@ public class Spinner extends Mob {
damage = super.attackProc( enemy, damage );
if (Random.Int(2) == 0) {
Buff.affect(enemy, Poison.class).set(Random.Int(7, 9) );
webCoolDown = 0;
state = FLEEING;
}
return damage;
}
private boolean shotWebVisually = false;
@Override
public void move(int step) {
int curWeb = Blob.volumeAt(pos, Web.class);
if (state == FLEEING && curWeb < 5) {
GameScene.add(Blob.seed(pos, Random.Int(5, 7) - curWeb, Web.class));
if (enemySeen && webCoolDown <= 0 && lastEnemyPos != -1){
if (webPos() != -1){
if (sprite != null && (sprite.visible || enemy.sprite.visible)) {
sprite.zap( webPos() );
shotWebVisually = true;
} else {
shootWeb();
}
}
}
super.move(step);
}
public int webPos(){
Ballistica b;
//aims web in direction enemy is moving, or between self and enemy if they aren't moving
if (lastEnemyPos == enemy.pos){
b = new Ballistica( enemy.pos, pos, Ballistica.WONT_STOP );
} else {
b = new Ballistica( lastEnemyPos, enemy.pos, Ballistica.WONT_STOP );
}
int collisionIndex = 0;
for (int i = 0; i < b.path.size(); i++){
if (b.path.get(i) == enemy.pos){
collisionIndex = i;
break;
}
}
int webPos = b.path.get( collisionIndex+1 );
if (Dungeon.level.passable[webPos]){
return webPos;
} else {
return -1;
}
}
public void shootWeb(){
int webPos = webPos();
if (webPos != enemy.pos && webPos != -1){
int i;
for ( i = 0; i < PathFinder.CIRCLE8.length; i++){
if ((enemy.pos + PathFinder.CIRCLE8[i]) == webPos){
break;
}
}
//spread to the tile hero was moving towards and the two adjacent ones
int leftPos = enemy.pos + PathFinder.CIRCLE8[left(i)];
int rightPos = enemy.pos + PathFinder.CIRCLE8[right(i)];
if (Dungeon.level.passable[leftPos]) GameScene.add(Blob.seed(leftPos, 20, Web.class));
if (Dungeon.level.passable[webPos]) GameScene.add(Blob.seed(webPos, 20, Web.class));
if (Dungeon.level.passable[rightPos])GameScene.add(Blob.seed(rightPos, 20, Web.class));
webCoolDown = 10;
}
next();
}
private int left(int direction){
return direction == 0 ? 7 : direction-1;
}
private int right(int direction){
return direction == 7 ? 0 : direction+1;
}
{
resistances.add(Poison.class);
}

View File

@ -60,6 +60,6 @@ public class WebParticle extends PixelParticle {
float p = left / lifespan;
am = p < 0.5f ? p : 1 - p;
scale.y = 16 + p * 8;
scale.y = 12 + p * 6;
}
}

View File

@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SmokeScreen;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Web;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.WellWater;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Awareness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
@ -37,6 +38,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LockedFloor;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicalSight;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MindVision;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Shadows;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
@ -590,6 +592,13 @@ public abstract class Level implements Bundlable {
}
}
Web w = (Web) blobs.get(Web.class);
if (w != null && w.volume > 0){
for (int i=0; i < length(); i++) {
solid[i] = solid[i] || w.cur[i] > 0;
}
}
int lastRow = length() - width();
for (int i=0; i < width(); i++) {
passable[i] = avoid[i] = false;
@ -770,6 +779,11 @@ public abstract class Level implements Bundlable {
}
public void occupyCell( Char ch ){
if (!ch.isImmune(Web.class) && Blob.volumeAt(ch.pos, Web.class) > 0){
blobs.get(Web.class).clear(ch.pos);
Web.affectChar( ch );
}
if (!ch.flying){
if (pit[ch.pos]){
@ -867,6 +881,10 @@ public abstract class Level implements Bundlable {
if (plant != null) {
plant.trigger();
}
if (hard && Blob.volumeAt(cell, Web.class) > 0){
blobs.get(Web.class).clear(cell);
}
}
public void updateFieldOfView( Char c, boolean[] fieldOfView ) {

View File

@ -23,8 +23,13 @@ package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Spinner;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.watabou.noosa.TextureFilm;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback;
//TODO improvements here
public class SpinnerSprite extends MobSprite {
public SpinnerSprite() {
@ -45,6 +50,8 @@ public class SpinnerSprite extends MobSprite {
attack = new Animation( 12, false );
attack.frames( frames, 0, 4, 5, 0 );
zap = attack.clone();
die = new Animation( 12, false );
die.frames( frames, 6, 7, 8, 9 );
@ -58,6 +65,32 @@ public class SpinnerSprite extends MobSprite {
renderShadow = false;
}
public void zap( int cell ) {
turnTo( ch.pos , cell );
play( zap );
MagicMissile.boltFromChar( parent,
MagicMissile.MAGIC_MISSILE,
this,
cell,
new Callback() {
@Override
public void call() {
((Spinner)ch).shootWeb();
}
} );
Sample.INSTANCE.play( Assets.SND_MISS );
}
@Override
public void onComplete( Animation anim ) {
if (anim == zap) {
play( run );
}
super.onComplete( anim );
}
@Override
public int blood() {
return 0xFFBFE5B8;

View File

@ -39,7 +39,7 @@ actors.blobs.waterofhealth.desc=Power of health radiates from the water of this
actors.blobs.wateroftransmutation.desc=Power of change radiates from the water of this well. Throw an item into the well to turn it into something else.
actors.blobs.web.desc=Everything is covered with a thick web here.
actors.blobs.web.desc=A thick web is covering everything here. Anything that touches or is thrown though the web will break it, but will also be stuck in place.
@ -596,7 +596,7 @@ actors.mobs.snake.name=sewer snake
actors.mobs.snake.desc=These oversized serpents are capable of quickly slithering around blows, making them quite hard to hit. Magical attacks or surprise attacks are capable of catching them off-guard however.\n\nYou can perform a surprise attack by attacking while out of the snake's vision. One way is to let a snake chase you through a doorway and then _strike just as it moves into the door._
actors.mobs.spinner.name=cave spinner
actors.mobs.spinner.desc=These greenish furry cave spiders try to avoid direct combat, preferring to wait in the distance while their victim, entangled in the spinner's excreted cobweb, slowly dies from their poisonous bite.
actors.mobs.spinner.desc=These greenish furry cave spiders try to avoid direct combat. Instead they prefer to wait in the distance while their victim struggles in their excreted cobweb, slowly dieing from their poisonous bite. They are capable of shooting their webs great distances, and will try to block whatever path their prey is taking.
actors.mobs.statue.name=animated statue
actors.mobs.statue.def_verb=blocked