v3.0.0: big chaotic censer buff: now warns and only spews at enemies
This commit is contained in:
@@ -1,49 +1,67 @@
|
||||
###blobs
|
||||
actors.blobs.blizzard.name=blizzard
|
||||
actors.blobs.blizzard.desc=A blizzard is swirling here.
|
||||
|
||||
actors.blobs.confusiongas.name=confusion gas
|
||||
actors.blobs.confusiongas.desc=A cloud of confusion gas is swirling here.
|
||||
|
||||
actors.blobs.electricity.name=electricity
|
||||
actors.blobs.electricity.desc=A field of electricity is sparking brightly here.
|
||||
actors.blobs.electricity.rankings_desc=Electrocuted
|
||||
actors.blobs.electricity.ondeath=You were shocked to death...
|
||||
|
||||
actors.blobs.fire.name=fire
|
||||
actors.blobs.fire.desc=A fire is raging here.
|
||||
|
||||
actors.blobs.foliage.name=foliage
|
||||
actors.blobs.foliage.desc=Shafts of light pierce the gloom of the underground garden.
|
||||
|
||||
actors.blobs.freezing.name=freezing air
|
||||
actors.blobs.freezing.desc=The air is unnaturally frigid here.
|
||||
|
||||
actors.blobs.goowarn.name=dark energy
|
||||
actors.blobs.goowarn.desc=Specks of dark energy are swarming here!
|
||||
|
||||
actors.blobs.inferno.name=inferno
|
||||
actors.blobs.inferno.desc=An inferno is raging here.
|
||||
|
||||
actors.blobs.paralyticgas.name=paralytic gas
|
||||
actors.blobs.paralyticgas.desc=A cloud of paralytic gas is swirling here.
|
||||
|
||||
actors.blobs.regrowth.name=regrowth
|
||||
|
||||
actors.blobs.sacrificialfire.name=sacrificial fire
|
||||
actors.blobs.sacrificialfire.desc=There is an altar here with a sacrificial fire burning atop it. Any creature that's killed here will be consumed as an offering to the spirits of the dungeon.\n\nPerhaps a reward will be given if enough sacrifices are made?
|
||||
actors.blobs.sacrificialfire.worthy=The fire consumes your offering and grows stronger.
|
||||
actors.blobs.sacrificialfire.unworthy=The fire consumes your offering, but doesn't change.
|
||||
actors.blobs.sacrificialfire.reward=The fire flares and then dissipates, leaving a reward behind!
|
||||
|
||||
actors.blobs.smokescreen.name=smokescreen
|
||||
actors.blobs.smokescreen.desc=A cloud of thick black smoke is swirling here.
|
||||
|
||||
actors.blobs.stenchgas.name=stench gas
|
||||
actors.blobs.stenchgas.desc=A cloud of fetid stench is swirling here.
|
||||
|
||||
actors.blobs.stormcloud.name=storm clouds
|
||||
actors.blobs.stormcloud.desc=A cloud of billowing water vapor is swirling here.
|
||||
|
||||
actors.blobs.toxicgas.name=toxic gas
|
||||
actors.blobs.toxicgas.desc=A greenish cloud of toxic gas is swirling here.
|
||||
actors.blobs.toxicgas.rankings_desc=Suffocated
|
||||
actors.blobs.toxicgas.ondeath=You died from the toxic gas...
|
||||
|
||||
actors.blobs.corrosivegas.name=corrosive gas
|
||||
actors.blobs.corrosivegas.desc=A cloud of deadly caustic gas is swirling here.
|
||||
|
||||
actors.blobs.waterofawareness.name=water of awareness
|
||||
actors.blobs.waterofawareness.procced=As you take a sip, you feel knowledge pour into your mind.
|
||||
actors.blobs.waterofawareness.desc=Power of knowledge radiates from the water of this well. Drinking from the well will fully identify all equipped items, identify curses on all items in your inventory, and reveal all items on the current floor.
|
||||
|
||||
actors.blobs.waterofhealth.name=water of health
|
||||
actors.blobs.waterofhealth.procced=As you take a sip, you feel your wounds heal completely.
|
||||
actors.blobs.waterofhealth.desc=Power of health radiates from the water of this well. Drinking from this well will heal your wounds, satisfy hunger, and cleanse curses on any worn items.
|
||||
|
||||
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.name=spider web
|
||||
actors.blobs.web.desc=A thick web is covering everything here. Anything that touches or is thrown through the web will break it, but will also be stuck in place.
|
||||
|
||||
|
||||
|
||||
@@ -1341,9 +1341,10 @@ items.stones.stoneofshock.desc=This runestone unleashes a blast of electrical en
|
||||
|
||||
###trinkets
|
||||
items.trinkets.chaoticcenser.name=chaotic censer
|
||||
items.trinkets.chaoticcenser.desc=After some time in the alchemy pot this incense-burning censer appears to be producing smoke all on its own! These gasses build up and will spew forth from the censer in random directions and semi-random intervals. It seems capable of producing all sorts of gasses, but the position they shoot out in seems to be more likely to be in your favour at least.
|
||||
items.trinkets.chaoticcenser.typical_stats_desc=Typically this trinket will spawn a harmful gas nearby roughly every _%d_ turns. The gas is more likely to appear when enemies are present, and less likely to appear in enclosed spaces. At higher levels these gases are more likely to be exotic and powerful.
|
||||
items.trinkets.chaoticcenser.stats_desc=At its current level, this trinket will spawn a harmful gas nearby roughly every _%d_ turns. The gas is more likely to appear when enemies are present, and less likely to appear in enclosed spaces. At higher levels these gases are more likely to be exotic and powerful.
|
||||
items.trinkets.chaoticcenser.spew=Your censer is about to spew: %s.
|
||||
items.trinkets.chaoticcenser.desc=After some time in the alchemy pot this incense-burning censer appears to be producing smoke all on its own! These gasses build up and will spew forth from the censer toward enemies at semi-random intervals. It seems capable of producing all sorts of gasses, but you'll get a moment of warning just before the censer activates.
|
||||
items.trinkets.chaoticcenser.typical_stats_desc=Typically this trinket will spawn a harmful gas near an enemy roughly every _%d_ turns. Gasses will only appear when enemies are present. At higher levels these gases are more likely to be exotic and powerful.
|
||||
items.trinkets.chaoticcenser.stats_desc=At its current level, this trinket will spawn a harmful gas near an enemy roughly every _%d_ turns. Gasses will only appear when enemies are present. At higher levels these gases are more likely to be exotic and powerful.
|
||||
|
||||
items.trinkets.dimensionalsundial.name=dimensional sundial
|
||||
items.trinkets.dimensionalsundial.warning=Your sundial isn't casting a shadow, you feel uneasy.
|
||||
|
||||
@@ -23,7 +23,6 @@ package com.shatteredpixel.shatteredpixeldungeon.items.trinkets;
|
||||
|
||||
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.blobs.Blizzard;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
|
||||
@@ -36,15 +35,16 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.StenchGas;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.StormCloud;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Regeneration;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Shopkeeper;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.TargetedCell;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TargetHealthIndicator;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.utils.BArray;
|
||||
import com.watabou.utils.Bundle;
|
||||
@@ -99,91 +99,42 @@ public class ChaoticCenser extends Trinket {
|
||||
if (avgTurns == -1){
|
||||
spend(Random.NormalIntRange(1, 5));
|
||||
return true;
|
||||
} else if (left > avgTurns*1.1f){
|
||||
left = Random.IntRange((int) (avgTurns*0.9f), (int) (avgTurns*1.1f));
|
||||
} else if (left > avgTurns*1.2f){
|
||||
left = Random.IntRange((int) (avgTurns*0.833f), (int) (avgTurns*1.2f));
|
||||
}
|
||||
|
||||
float triggerChance = 0;
|
||||
if (left > 0 && left <= 30) {
|
||||
if (left <= 0) {
|
||||
|
||||
Char enemy = null;
|
||||
|
||||
if (TargetHealthIndicator.instance != null && TargetHealthIndicator.instance.isVisible()
|
||||
&& TargetHealthIndicator.instance.target() != null
|
||||
&& TargetHealthIndicator.instance.target().alignment == Char.Alignment.ENEMY
|
||||
&& TargetHealthIndicator.instance.target().isAlive()) {
|
||||
triggerChance = 0.75f;
|
||||
}
|
||||
|
||||
} else if (left > -30 && left <= 0) {
|
||||
|
||||
if (TargetHealthIndicator.instance != null && TargetHealthIndicator.instance.isVisible()
|
||||
&& TargetHealthIndicator.instance.target() != null
|
||||
&& TargetHealthIndicator.instance.target().alignment == Char.Alignment.ENEMY
|
||||
&& TargetHealthIndicator.instance.target().isAlive()) {
|
||||
triggerChance = 1f;
|
||||
} else if (Dungeon.level.openSpace[target.pos]){
|
||||
triggerChance = 0.2f;
|
||||
}
|
||||
|
||||
} else if (left <= -30) {
|
||||
triggerChance = 1f;
|
||||
|
||||
}
|
||||
|
||||
if (triggerChance > 0) {
|
||||
if (safeAreaDelay >= 0) {
|
||||
boolean safeArea = false;
|
||||
|
||||
//shops are a safe area
|
||||
for (Char ch : Actor.chars()) {
|
||||
if (ch instanceof Shopkeeper
|
||||
&& Dungeon.level.distance(target.pos, ch.pos) <= 6
|
||||
&& new Ballistica(target.pos, ch.pos, Ballistica.PROJECTILE).collisionPos == ch.pos) {
|
||||
safeArea = true;
|
||||
}
|
||||
}
|
||||
|
||||
//enclosed spaces are a safe area if no enemies are present
|
||||
if ((TargetHealthIndicator.instance == null || TargetHealthIndicator.instance.target() == null
|
||||
|| TargetHealthIndicator.instance.target().alignment != Char.Alignment.ENEMY
|
||||
|| !TargetHealthIndicator.instance.target().isAlive())
|
||||
&& !Dungeon.level.openSpace[target.pos]) {
|
||||
safeArea = true;
|
||||
}
|
||||
|
||||
if (safeArea){
|
||||
int delay = Random.NormalIntRange(1, 5);
|
||||
spend(delay);
|
||||
safeAreaDelay -= delay;
|
||||
return true;
|
||||
if (produceGas(TargetHealthIndicator.instance.target())){
|
||||
Sample.INSTANCE.play(Assets.Sounds.GAS, 0.5f);
|
||||
Dungeon.hero.interrupt();
|
||||
left += Random.IntRange((int) (avgTurns * 0.9f), (int) (avgTurns * 1.1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Random.Float() < triggerChance){
|
||||
if (produceGas()) {
|
||||
Sample.INSTANCE.play(Assets.Sounds.GAS);
|
||||
Dungeon.hero.interrupt();
|
||||
left += Random.IntRange((int) (avgTurns * 0.9f), (int) (avgTurns * 1.1f));
|
||||
}
|
||||
}
|
||||
|
||||
//buff ticks an average of every 3 turns
|
||||
int delay = Random.NormalIntRange(1, 5);
|
||||
int delay = Random.NormalIntRange(1, 3);
|
||||
spend(delay);
|
||||
safeAreaDelay = Math.min(safeAreaDelay+2*delay, 100);
|
||||
left -= delay;
|
||||
left = (int)Math.max(left-delay, -avgTurns/3f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String LEFT = "left";
|
||||
private static String SAFE_AREA_DELAY = "safe_area_delay";
|
||||
|
||||
@Override
|
||||
public void storeInBundle(Bundle bundle) {
|
||||
super.storeInBundle(bundle);
|
||||
bundle.put(LEFT, left);
|
||||
bundle.put(SAFE_AREA_DELAY, safeAreaDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,12 +142,11 @@ public class ChaoticCenser extends Trinket {
|
||||
super.restoreFromBundle(bundle);
|
||||
if (bundle.contains(LEFT)){
|
||||
left = bundle.getInt(LEFT);
|
||||
safeAreaDelay = bundle.getInt(SAFE_AREA_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean produceGas(){
|
||||
private static boolean produceGas( Char target ){
|
||||
int level = trinketLevel(ChaoticCenser.class);
|
||||
|
||||
if (level < 0 || level > 3){
|
||||
@@ -222,69 +172,53 @@ public class ChaoticCenser extends Trinket {
|
||||
break;
|
||||
}
|
||||
|
||||
Char target = null;
|
||||
if (TargetHealthIndicator.instance != null && TargetHealthIndicator.instance.isVisible()
|
||||
&& TargetHealthIndicator.instance.target() != null
|
||||
&& TargetHealthIndicator.instance.target().alignment == Char.Alignment.ENEMY
|
||||
&& TargetHealthIndicator.instance.target().isAlive()) {
|
||||
target = TargetHealthIndicator.instance.target();
|
||||
}
|
||||
|
||||
HashMap<Integer, Float> candidateCells = new HashMap<>();
|
||||
PathFinder.buildDistanceMap(Dungeon.hero.pos, BArray.not(Dungeon.level.solid, null), 5);
|
||||
PathFinder.buildDistanceMap(Dungeon.hero.pos, BArray.not(Dungeon.level.solid, null), 6);
|
||||
|
||||
//spawn gas in a random visible cell 2-5 tiles away, likelihood is 3,4 > 2,5
|
||||
//spawn gas in a random visible cell 2-6 tiles away
|
||||
for (int i = 0; i < Dungeon.level.length(); i++){
|
||||
if (Dungeon.level.heroFOV[i] && PathFinder.distance[i] < Integer.MAX_VALUE) {
|
||||
switch (PathFinder.distance[i]) {
|
||||
case 3:
|
||||
case 4:
|
||||
candidateCells.put(i, 2f);
|
||||
break;
|
||||
case 2:
|
||||
case 5:
|
||||
candidateCells.put(i, 1f);
|
||||
break;
|
||||
if (PathFinder.distance[i] >= 2 && PathFinder.distance[i] <= 6) {
|
||||
candidateCells.put(i, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//unless we have a target, then strongly prefer cells closer to target
|
||||
if (target != null){
|
||||
int targetpos = target.pos;
|
||||
if (Dungeon.level.trueDistance(target.pos, Dungeon.hero.pos) >= 4){
|
||||
//if target is a distance from the hero, aim in front of them instead
|
||||
for (int i : PathFinder.NEIGHBOURS8){
|
||||
while (!Dungeon.level.solid[targetpos+i]
|
||||
&& Dungeon.level.trueDistance(target.pos+i, Dungeon.hero.pos) < Dungeon.level.trueDistance(targetpos, Dungeon.hero.pos)){
|
||||
targetpos = target.pos+i;
|
||||
}
|
||||
//strongly prefer cells closer to target
|
||||
int targetpos = target.pos;
|
||||
if (Dungeon.level.trueDistance(target.pos, Dungeon.hero.pos) >= 4){
|
||||
//if target is a distance from the hero, aim in front of them instead
|
||||
for (int i : PathFinder.NEIGHBOURS8){
|
||||
while (!Dungeon.level.solid[targetpos+i]
|
||||
&& Dungeon.level.trueDistance(target.pos+i, Dungeon.hero.pos) < Dungeon.level.trueDistance(targetpos, Dungeon.hero.pos)){
|
||||
targetpos = target.pos+i;
|
||||
}
|
||||
}
|
||||
float closest = 100;
|
||||
for (int cell : candidateCells.keySet()){
|
||||
float dist = Dungeon.level.distance(cell, targetpos);
|
||||
if (dist < closest){
|
||||
closest = dist;
|
||||
}
|
||||
}
|
||||
float closest = 100;
|
||||
for (int cell : candidateCells.keySet()){
|
||||
float dist = Dungeon.level.distance(cell, targetpos);
|
||||
if (dist < closest){
|
||||
closest = dist;
|
||||
}
|
||||
for (int cell : candidateCells.keySet()){
|
||||
float dist = Dungeon.level.distance(cell, targetpos);
|
||||
if (dist - closest == 0) {
|
||||
candidateCells.put(cell, 4f);
|
||||
} else if (dist - closest <= 1) {
|
||||
candidateCells.put(cell, 1f);
|
||||
} else {
|
||||
candidateCells.put(cell, 0f);
|
||||
}
|
||||
}
|
||||
for (int cell : candidateCells.keySet()){
|
||||
float dist = Dungeon.level.distance(cell, targetpos);
|
||||
if (dist - closest == 0) {
|
||||
candidateCells.put(cell, 8f);
|
||||
} else if (dist - closest <= 1) {
|
||||
candidateCells.put(cell, 1f);
|
||||
} else {
|
||||
candidateCells.put(cell, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidateCells.isEmpty()) {
|
||||
Integer targetCell = Random.chances(candidateCells);
|
||||
if (targetCell != null) {
|
||||
GameScene.add(Blob.seed(targetCell, (int) gasQuantity, gasToSpawn));
|
||||
MagicMissile.boltFromChar(Dungeon.hero.sprite.parent, MISSILE_VFX.get(gasToSpawn), Dungeon.hero.sprite, targetCell, null);
|
||||
Buff.affect(Dungeon.hero, GasSpewer.class, Dungeon.hero.cooldown()).set(targetCell, gasToSpawn, (int)gasQuantity);
|
||||
GLog.w(Messages.get(ChaoticCenser.class, "spew", Messages.titleCase(Messages.get(gasToSpawn, "name")) ));
|
||||
target.sprite.parent.addToBack(new TargetedCell(targetCell, 0xFF0000));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -293,6 +227,66 @@ public class ChaoticCenser extends Trinket {
|
||||
|
||||
}
|
||||
|
||||
public static class GasSpewer extends FlavourBuff {
|
||||
|
||||
private int targetCell;
|
||||
|
||||
private int depth;
|
||||
private int branch;
|
||||
|
||||
private Class<?extends Blob> gasType;
|
||||
private int gasQuantity;
|
||||
|
||||
public void set( int targetCell, Class<?extends Blob> gasType, int gasQuantity){
|
||||
this.targetCell = targetCell;
|
||||
|
||||
depth = Dungeon.depth;
|
||||
branch = Dungeon.branch;
|
||||
|
||||
this.gasType = gasType;
|
||||
this.gasQuantity = gasQuantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean act() {
|
||||
|
||||
if (depth == Dungeon.depth && branch == Dungeon.branch){
|
||||
GameScene.add(Blob.seed(targetCell, gasQuantity, gasType));
|
||||
MagicMissile.boltFromChar(Dungeon.hero.sprite.parent, MISSILE_VFX.get(gasType), Dungeon.hero.sprite, targetCell, null);
|
||||
Sample.INSTANCE.play(Assets.Sounds.GAS);
|
||||
}
|
||||
|
||||
detach();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final String CELL = "cell";
|
||||
private static final String DEPTH = "depth";
|
||||
private static final String BRANCH = "branch";
|
||||
private static final String GAS_TYPE = "gas_type";
|
||||
private static final String GAS_QUANTITY = "gas_quantity";
|
||||
|
||||
@Override
|
||||
public void storeInBundle(Bundle bundle) {
|
||||
super.storeInBundle(bundle);
|
||||
bundle.put(CELL, targetCell);
|
||||
bundle.put(DEPTH, depth);
|
||||
bundle.put(BRANCH, branch);
|
||||
bundle.put(GAS_TYPE, gasType);
|
||||
bundle.put(GAS_QUANTITY, gasQuantity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
targetCell = bundle.getInt(CELL);
|
||||
depth = bundle.getInt(DEPTH);
|
||||
branch = bundle.getInt(BRANCH);
|
||||
gasType = bundle.getClass(GAS_TYPE);
|
||||
gasQuantity = bundle.getInt(GAS_QUANTITY);
|
||||
}
|
||||
}
|
||||
|
||||
private static final float[][] GAS_CAT_CHANCES = new float[4][3];
|
||||
static {
|
||||
GAS_CAT_CHANCES[0] = new float[]{70, 25, 5};
|
||||
|
||||
Reference in New Issue
Block a user