diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 0c01eaf78..e6418ecfe 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -19,6 +19,11 @@ actors.blobs.inferno.desc=An inferno is raging here. actors.blobs.paralyticgas.desc=A cloud of paralytic gas is swirling here. +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 mede? +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.desc=A cloud of thick black smoke is swirling here. actors.blobs.stenchgas.desc=A cloud of fetid stench is swirling here. diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/SacrificialFire.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/SacrificialFire.java new file mode 100644 index 000000000..3596a5f44 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/SacrificialFire.java @@ -0,0 +1,162 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2022 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.actors.blobs; + +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.FlavourBuff; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bee; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Piranha; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Statue; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Wraith; +import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SacrificialParticle; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SacrificeRoom; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; + +public class SacrificialFire extends Blob { + + BlobEmitter curEmitter; + + { + //acts after mobs, so they can get marked as they move + actPriority = MOB_PRIO-1; + } + + @Override + protected void evolve() { + int cell; + for (int i=area.top-1; i <= area.bottom; i++) { + for (int j = area.left-1; j <= area.right; j++) { + cell = j + i* Dungeon.level.width(); + if (Dungeon.level.insideMap(cell)) { + off[cell] = cur[cell]; + volume += off[cell]; + + Char ch = Actor.findChar( cell ); + if (ch != null && off[cell] > 0){ + if (Dungeon.level.heroFOV[cell] && ch.buff( Marked.class ) == null) { + CellEmitter.get(cell).burst( SacrificialParticle.FACTORY, 5 ); + } + Buff.prolong( ch, Marked.class, Marked.DURATION ); + } + } + } + } + + //a bit brittle, assumes only one tile of sacrificial fire can exist per floor + int max = 5 + Dungeon.depth * 5; + curEmitter.pour( SacrificialParticle.FACTORY, 0.01f + ((volume / (float)max) * 0.09f) ); + } + + @Override + public void use( BlobEmitter emitter ) { + super.use( emitter ); + curEmitter = emitter; + + //a bit brittle, assumes only one tile of sacrificial fire can exist per floor + int max = 5 + Dungeon.depth * 5; + curEmitter.pour( SacrificialParticle.FACTORY, 0.01f + ((volume / (float)max) * 0.09f) ); + } + + @Override + public String tileDesc() { + return Messages.get(this, "desc"); + } + + public static void sacrifice( Char ch ) { + + SacrificialFire fire = (SacrificialFire)Dungeon.level.blobs.get( SacrificialFire.class ); + + if (fire != null && fire.cur[ch.pos] > 0) { + + int exp = 0; + if (ch instanceof Mob) { + //same rates as used in wand of corruption + if (ch instanceof Statue || ch instanceof Mimic){ + exp = 1 + Dungeon.depth; + } else if (ch instanceof Piranha || ch instanceof Bee) { + exp = 1 + Dungeon.depth/2; + } else if (ch instanceof Wraith) { + exp = 1 + Dungeon.depth/3; + } else { + exp = ((Mob)ch).EXP; + } + exp *= Random.IntRange( 2, 3 ); + } else if (ch instanceof Hero) { + exp = 1_000_000; //always enough to activate the reward, if you can somehow get it + } + + if (exp > 0) { + + int volume = fire.cur[ch.pos] - exp; + if (volume > 0) { + fire.cur[ch.pos] -= exp; + fire.volume -= exp; + CellEmitter.get(ch.pos).burst( SacrificialParticle.FACTORY, 20 ); + Sample.INSTANCE.play(Assets.Sounds.BURNING ); + GLog.w( Messages.get(SacrificialFire.class, "worthy")); + } else { + fire.clear(ch.pos); + + for (int i : PathFinder.NEIGHBOURS9){ + CellEmitter.get(ch.pos+i).burst( SacrificialParticle.FACTORY, 20 ); + } + Sample.INSTANCE.play(Assets.Sounds.BURNING ); + Sample.INSTANCE.play(Assets.Sounds.BURNING ); + Sample.INSTANCE.play(Assets.Sounds.BURNING ); + GLog.w( Messages.get(SacrificialFire.class, "reward")); + Dungeon.level.drop( SacrificeRoom.prize( Dungeon.level ), ch.pos ).sprite.drop(); + } + } else { + + GLog.w( Messages.get(SacrificialFire.class, "unworthy")); + + } + } + } + + public static class Marked extends FlavourBuff { + + public static final float DURATION = 2f; + + @Override + public void detach() { + if (!target.isAlive()) { + sacrifice( target ); + } + super.detach(); + } + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SacrificeRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SacrificeRoom.java new file mode 100644 index 000000000..de37ded35 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SacrificeRoom.java @@ -0,0 +1,88 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2022 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.levels.rooms.special; + +import com.shatteredpixel.shatteredpixeldungeon.Challenges; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SacrificialFire; +import com.shatteredpixel.shatteredpixeldungeon.items.Generator; +import com.shatteredpixel.shatteredpixeldungeon.items.Gold; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; +import com.shatteredpixel.shatteredpixeldungeon.levels.Level; +import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; +import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter; +import com.watabou.utils.Point; + +public class SacrificeRoom extends SpecialRoom { + + @Override + public void paint(Level level) { + Painter.fill( level, this, Terrain.WALL ); + Painter.fill( level, this, 1, Terrain.CHASM ); + + Point c = center(); + Door door = entrance(); + if (door.x == left || door.x == right) { + Point p = Painter.drawInside( level, this, door, Math.abs( door.x - c.x ) - 2, Terrain.EMPTY_SP ); + for (; p.y != c.y; p.y += p.y < c.y ? +1 : -1) { + Painter.set( level, p, Terrain.EMPTY_SP ); + } + } else { + Point p = Painter.drawInside( level, this, door, Math.abs( door.y - c.y ) - 2, Terrain.EMPTY_SP ); + for (; p.x != c.x; p.x += p.x < c.x ? +1 : -1) { + Painter.set( level, p, Terrain.EMPTY_SP ); + } + } + + Painter.fill( level, c.x - 1, c.y - 1, 3, 3, Terrain.EMBERS ); + Painter.set( level, c, Terrain.PEDESTAL ); + + Blob.seed( level.pointToCell(c), 5 + Dungeon.depth * 5, SacrificialFire.class, level ); + + door.set( Door.Type.EMPTY ); + } + + public static Item prize( Level level ) { + + //1 floor set higher than normal + Weapon prize = Generator.randomWeapon( (Dungeon.depth / 5) + 1); + + if (Challenges.isItemBlocked(prize)){ + return new Gold().random(); + } + + //if it isn't already cursed, give it a free upgrade + if (!prize.cursed){ + prize.upgrade(); + //curse the weapon, unless it has a glyph + if (!prize.hasGoodEnchant()){ + prize.enchant(Weapon.Enchantment.randomCurse()); + } + } + prize.cursed = prize.cursedKnown = true; + + return prize; + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SpecialRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SpecialRoom.java index 8bb0398b3..2468f5f0c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SpecialRoom.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/SpecialRoom.java @@ -119,8 +119,6 @@ public abstract class SpecialRoom extends Room { if (!runConsSpecials.isEmpty()) runSpecials.add(runConsSpecials.remove(0)); } - runSpecials.add(0, WeakFloorRoom.class); - pitNeededDepth = -1; }