v0.8.2: improvements to cone AOEs and fireblast:

- items which use cones can now trickshot
- fireblast now bursts open doors instead of colliding with them
This commit is contained in:
Evan Debenham 2020-07-22 00:48:33 -04:00
parent 2895c212f7
commit 16822095df
6 changed files with 80 additions and 19 deletions

View File

@ -144,7 +144,7 @@ public class TalismanOfForesight extends Artifact {
//starts at 200 degrees, loses 8% per tile of distance //starts at 200 degrees, loses 8% per tile of distance
float angle = Math.round(200*(float)Math.pow(0.92, dist)); float angle = Math.round(200*(float)Math.pow(0.92, dist));
ConeAOE cone = new ConeAOE(curUser.pos, target, angle); ConeAOE cone = new ConeAOE(new Ballistica(curUser.pos, target, Ballistica.STOP_TARGET), angle);
int earnedExp = 0; int earnedExp = 0;
boolean noticed = false; boolean noticed = false;

View File

@ -40,6 +40,9 @@ import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback; import com.watabou.utils.Callback;
import com.watabou.utils.PathFinder;
import java.util.ArrayList;
public class PotionOfDragonsBreath extends ExoticPotion { public class PotionOfDragonsBreath extends ExoticPotion {
@ -77,12 +80,12 @@ public class PotionOfDragonsBreath extends ExoticPotion {
curUser.sprite.zap(cell); curUser.sprite.zap(cell);
Sample.INSTANCE.play( Assets.Sounds.BURNING ); Sample.INSTANCE.play( Assets.Sounds.BURNING );
final Ballistica bolt = new Ballistica(curUser.pos, cell, Ballistica.STOP_TERRAIN); final Ballistica bolt = new Ballistica(curUser.pos, cell, Ballistica.STOP_TERRAIN | Ballistica.OPEN_DOORS);
int maxDist = 6; int maxDist = 6;
int dist = Math.min(bolt.dist, maxDist); int dist = Math.min(bolt.dist, maxDist);
final ConeAOE cone = new ConeAOE(curUser.pos, bolt.path.get(dist), 6, 60, Ballistica.STOP_TERRAIN | Ballistica.STOP_TARGET ); final ConeAOE cone = new ConeAOE(bolt, 6, 60, Ballistica.STOP_TERRAIN | Ballistica.STOP_TARGET | Ballistica.OPEN_DOORS );
//cast to cells at the tip, rather than all cells, better performance. //cast to cells at the tip, rather than all cells, better performance.
for (Ballistica ray : cone.rays){ for (Ballistica ray : cone.rays){
@ -101,6 +104,7 @@ public class PotionOfDragonsBreath extends ExoticPotion {
new Callback() { new Callback() {
@Override @Override
public void call() { public void call() {
ArrayList<Integer> adjacentCells = new ArrayList<>();
for (int cell : cone.cells){ for (int cell : cone.cells){
//ignore caster cell //ignore caster cell
if (cell == bolt.sourcePos){ if (cell == bolt.sourcePos){
@ -108,7 +112,9 @@ public class PotionOfDragonsBreath extends ExoticPotion {
} }
//only ignite cells directly near caster if they are flammable //only ignite cells directly near caster if they are flammable
if (!Dungeon.level.adjacent(bolt.sourcePos, cell) || Dungeon.level.flamable[cell]){ if (Dungeon.level.adjacent(bolt.sourcePos, cell) && !Dungeon.level.flamable[cell]){
adjacentCells.add(cell);
} else {
GameScene.add( Blob.seed( cell, 5, Fire.class ) ); GameScene.add( Blob.seed( cell, 5, Fire.class ) );
} }
@ -119,6 +125,19 @@ public class PotionOfDragonsBreath extends ExoticPotion {
Buff.affect(ch, Cripple.class, 5f); Buff.affect(ch, Cripple.class, 5f);
} }
} }
//ignite cells that share a side with an adjacent cell, are flammable, and are further from the source pos
//This prevents short-range casts not igniting barricades or bookshelves
for (int cell : adjacentCells){
for (int i : PathFinder.NEIGHBOURS4){
if (Dungeon.level.trueDistance(cell+i, bolt.sourcePos) > Dungeon.level.trueDistance(cell, bolt.sourcePos)
&& Dungeon.level.flamable[cell+i]
&& Fire.volumeAt(cell+i, Fire.class) == 0){
GameScene.add( Blob.seed( cell+i, 5, Fire.class ) );
}
}
}
curUser.next(); curUser.next();
} }
}); });

View File

@ -41,6 +41,7 @@ import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback; import com.watabou.utils.Callback;
import com.watabou.utils.PathFinder;
import java.util.ArrayList; import java.util.ArrayList;
@ -49,7 +50,7 @@ public class WandOfFireblast extends DamageWand {
{ {
image = ItemSpriteSheet.WAND_FIREBOLT; image = ItemSpriteSheet.WAND_FIREBOLT;
collisionProperties = Ballistica.STOP_TERRAIN; collisionProperties = Ballistica.STOP_TERRAIN | Ballistica.OPEN_DOORS;
} }
//1x/2x/3x damage //1x/2x/3x damage
@ -68,6 +69,7 @@ public class WandOfFireblast extends DamageWand {
protected void onZap( Ballistica bolt ) { protected void onZap( Ballistica bolt ) {
ArrayList<Char> affectedChars = new ArrayList<>(); ArrayList<Char> affectedChars = new ArrayList<>();
ArrayList<Integer> adjacentCells = new ArrayList<>();
for( int cell : cone.cells ){ for( int cell : cone.cells ){
//ignore caster cell //ignore caster cell
@ -76,7 +78,9 @@ public class WandOfFireblast extends DamageWand {
} }
//only ignite cells directly near caster if they are flammable //only ignite cells directly near caster if they are flammable
if (!Dungeon.level.adjacent(bolt.sourcePos, cell) || Dungeon.level.flamable[cell]){ if (Dungeon.level.adjacent(bolt.sourcePos, cell) && !Dungeon.level.flamable[cell]){
adjacentCells.add(cell);
} else {
GameScene.add( Blob.seed( cell, 1+chargesPerCast(), Fire.class ) ); GameScene.add( Blob.seed( cell, 1+chargesPerCast(), Fire.class ) );
} }
@ -86,6 +90,18 @@ public class WandOfFireblast extends DamageWand {
} }
} }
//ignite cells that share a side with an adjacent cell, are flammable, and are further from the source pos
//This prevents short-range casts not igniting barricades or bookshelves
for (int cell : adjacentCells){
for (int i : PathFinder.NEIGHBOURS4){
if (Dungeon.level.trueDistance(cell+i, bolt.sourcePos) > Dungeon.level.trueDistance(cell, bolt.sourcePos)
&& Dungeon.level.flamable[cell+i]
&& Fire.volumeAt(cell+i, Fire.class) == 0){
GameScene.add( Blob.seed( cell+i, 1+chargesPerCast(), Fire.class ) );
}
}
}
for ( Char ch : affectedChars ){ for ( Char ch : affectedChars ){
processSoulMark(ch, chargesPerCast()); processSoulMark(ch, chargesPerCast());
ch.damage(damageRoll(), this); ch.damage(damageRoll(), this);
@ -119,7 +135,7 @@ public class WandOfFireblast extends DamageWand {
int maxDist = 2 + 2*chargesPerCast(); int maxDist = 2 + 2*chargesPerCast();
int dist = Math.min(bolt.dist, maxDist); int dist = Math.min(bolt.dist, maxDist);
cone = new ConeAOE( bolt.sourcePos, bolt.path.get(dist), cone = new ConeAOE( bolt,
maxDist, maxDist,
30 + 20*chargesPerCast(), 30 + 20*chargesPerCast(),
collisionProperties | Ballistica.STOP_TARGET); collisionProperties | Ballistica.STOP_TARGET);

View File

@ -203,7 +203,7 @@ public class WandOfRegrowth extends Wand {
int maxDist = 2 + 2*chargesPerCast(); int maxDist = 2 + 2*chargesPerCast();
int dist = Math.min(bolt.dist, maxDist); int dist = Math.min(bolt.dist, maxDist);
cone = new ConeAOE( bolt.sourcePos, bolt.path.get(dist), cone = new ConeAOE( bolt,
maxDist, maxDist,
20 + 10*chargesPerCast(), 20 + 10*chargesPerCast(),
collisionProperties | Ballistica.STOP_TARGET); collisionProperties | Ballistica.STOP_TARGET);

View File

@ -24,6 +24,9 @@ package com.shatteredpixel.shatteredpixeldungeon.mechanics;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -38,9 +41,10 @@ public class Ballistica {
public Integer dist = 0; public Integer dist = 0;
//parameters to specify the colliding cell //parameters to specify the colliding cell
public static final int STOP_TARGET = 1; //ballistica will stop at the target cell public static final int STOP_TARGET = 1; //ballistica will stop at the target cell
public static final int STOP_CHARS = 2; //ballistica will stop on first char hit public static final int STOP_CHARS = 2; //ballistica will stop on first char hit
public static final int STOP_TERRAIN = 4; //ballistica will stop on terrain(LOS blocking, impassable, etc.) public static final int STOP_TERRAIN = 4; //ballistica will stop on solid terrain
public static final int OPEN_DOORS = 8; //ballistica will open doors instead of colliding
public static final int PROJECTILE = STOP_TARGET | STOP_CHARS | STOP_TERRAIN; public static final int PROJECTILE = STOP_TARGET | STOP_CHARS | STOP_TERRAIN;
@ -51,7 +55,12 @@ public class Ballistica {
public Ballistica( int from, int to, int params ){ public Ballistica( int from, int to, int params ){
sourcePos = from; sourcePos = from;
build(from, to, (params & STOP_TARGET) > 0, (params & STOP_CHARS) > 0, (params & STOP_TERRAIN) > 0); build(from, to,
(params & STOP_TARGET) > 0,
(params & STOP_CHARS) > 0,
(params & STOP_TERRAIN) > 0,
(params & OPEN_DOORS) > 0);
if (collisionPos != null) { if (collisionPos != null) {
dist = path.indexOf(collisionPos); dist = path.indexOf(collisionPos);
} else if (!path.isEmpty()) { } else if (!path.isEmpty()) {
@ -63,7 +72,7 @@ public class Ballistica {
} }
} }
private void build( int from, int to, boolean stopTarget, boolean stopChars, boolean stopTerrain ) { private void build( int from, int to, boolean stopTarget, boolean stopChars, boolean stopTerrain, boolean openDoors ) {
int w = Dungeon.level.width(); int w = Dungeon.level.width();
int x0 = from % w; int x0 = from % w;
@ -114,6 +123,11 @@ public class Ballistica {
path.add(cell); path.add(cell);
if (openDoors && collisionPos == null && Dungeon.level.map[cell] == Terrain.DOOR){
Level.set(cell, Terrain.OPEN_DOOR);
GameScene.updateMap(cell);
}
if ((stopTerrain && cell != sourcePos && Dungeon.level.solid[cell]) if ((stopTerrain && cell != sourcePos && Dungeon.level.solid[cell])
|| (cell != sourcePos && stopChars && Actor.findChar( cell ) != null) || (cell != sourcePos && stopChars && Actor.findChar( cell ) != null)
|| (cell == to && stopTarget)){ || (cell == to && stopTarget)){

View File

@ -33,21 +33,26 @@ import java.util.LinkedHashSet;
//a cone made of up several ballisticas scanning in an arc //a cone made of up several ballisticas scanning in an arc
public class ConeAOE { public class ConeAOE {
public Ballistica coreRay;
public ArrayList<Ballistica> rays = new ArrayList<>(); public ArrayList<Ballistica> rays = new ArrayList<>();
public HashSet<Integer> cells = new HashSet<>(); public HashSet<Integer> cells = new HashSet<>();
public ConeAOE( int from, int to, float degrees ){ public ConeAOE( Ballistica core, float degrees ){
this(from, to, Float.POSITIVE_INFINITY, degrees, Ballistica.STOP_TARGET); this( core, Float.POSITIVE_INFINITY, degrees, Ballistica.STOP_TARGET/* TODO */);
} }
public ConeAOE( int from, int to, float maxDist, float degrees, int ballisticaParams ){ public ConeAOE( Ballistica core, float maxDist, float degrees, int ballisticaParams ){
coreRay = core;
//we want to use true coordinates for our trig functions, not game cells //we want to use true coordinates for our trig functions, not game cells
// so get the center of from and to as points // so get the center of from and to as points
PointF fromP = new PointF(Dungeon.level.cellToPoint(from)); PointF fromP = new PointF(Dungeon.level.cellToPoint(core.sourcePos));
fromP.x += 0.5f; fromP.x += 0.5f;
fromP.y += 0.5f; fromP.y += 0.5f;
PointF toP = new PointF(Dungeon.level.cellToPoint(to));
PointF toP = new PointF(Dungeon.level.cellToPoint(core.collisionPos));
toP.x += 0.5f; toP.x += 0.5f;
toP.y += 0.5f; toP.y += 0.5f;
@ -94,11 +99,18 @@ public class ConeAOE {
//cast a ray to each found cell, these make up the cone //cast a ray to each found cell, these make up the cone
for( int c : targetCells ){ for( int c : targetCells ){
Ballistica ray = new Ballistica(from, c, ballisticaParams); Ballistica ray = new Ballistica(core.sourcePos, c, ballisticaParams);
cells.addAll(ray.subPath(1, ray.dist)); cells.addAll(ray.subPath(1, ray.dist));
rays.add(ray); rays.add(ray);
} }
//lastly add any cells in the core
for ( int c : core.subPath(1, core.dist)){
if (Dungeon.level.trueDistance(core.sourcePos, c) <= maxDist){
cells.add(c);
}
}
} }
} }