diff --git a/core/src/main/assets/ripper.png b/core/src/main/assets/ripper.png
new file mode 100644
index 000000000..f557bd822
Binary files /dev/null and b/core/src/main/assets/ripper.png differ
diff --git a/core/src/main/assets/spawner.png b/core/src/main/assets/spawner.png
new file mode 100644
index 000000000..a1f1caf8b
Binary files /dev/null and b/core/src/main/assets/spawner.png differ
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java
index f9aa1373d..07f5928b9 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java
@@ -103,6 +103,8 @@ public class Assets {
public static final String SNAKE = "snake.png";
public static final String NECRO = "necromancer.png";
public static final String GHOUL = "ghoul.png";
+ public static final String RIPPER = "ripper.png";
+ public static final String SPAWNER = "spawner.png";
public static final String ITEMS = "items.png";
public static final String TERRAIN_FEATURES = "terrain_features.png";
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java
index e5f8500db..396d69d0a 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java
@@ -93,7 +93,7 @@ public class Dungeon {
NECRO_HP,
BAT_HP,
WARLOCK_HP,
- SCORPIO_HP,
+ //Demon spawners are already limited in their spawnrate, no need to limit their health drops
//alchemy
COOKING_HP,
BLANDFRUIT_SEED,
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DemonSpawner.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DemonSpawner.java
new file mode 100644
index 000000000..4395145a3
--- /dev/null
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DemonSpawner.java
@@ -0,0 +1,102 @@
+/*
+ * Pixel Dungeon
+ * Copyright (C) 2012-2015 Oleg Dolya
+ *
+ * Shattered Pixel Dungeon
+ * Copyright (C) 2014-2019 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.mobs;
+
+import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
+import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
+import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
+import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
+import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
+import com.shatteredpixel.shatteredpixeldungeon.sprites.SpawnerSprite;
+import com.watabou.utils.PathFinder;
+import com.watabou.utils.Random;
+
+import java.util.ArrayList;
+
+public class DemonSpawner extends Mob {
+
+ {
+ spriteClass = SpawnerSprite.class;
+
+ HP = HT = 150;
+ defenseSkill = 0;
+
+ EXP = 25;
+ maxLvl = 29;
+
+ state = PASSIVE;
+
+ loot = PotionOfHealing.class;
+ lootChance = 1f;
+
+ properties.add(Property.IMMOVABLE);
+ properties.add(Property.MINIBOSS);
+ }
+
+ @Override
+ public int drRoll() {
+ return Random.NormalIntRange(0, 12);
+ }
+
+ private float spawnCooldown = 50;
+
+ @Override
+ protected boolean act() {
+ spawnCooldown--;
+ if (spawnCooldown <= 0){
+ ArrayList candidates = new ArrayList<>();
+ for (int n : PathFinder.NEIGHBOURS8) {
+ if (!Dungeon.level.solid[pos+n] && Actor.findChar( pos+n ) == null) {
+ candidates.add( pos+n );
+ }
+ }
+
+ if (!candidates.isEmpty()) {
+ RipperDemon spawn = new RipperDemon();
+
+ spawn.pos = Random.element( candidates );
+ spawn.state = spawn.HUNTING;
+
+ Dungeon.level.occupyCell(spawn);
+
+ GameScene.add( spawn );
+ if (sprite.visible) {
+ Actor.addDelayed(new Pushing(spawn, pos, spawn.pos), -1);
+ }
+
+ spawnCooldown += 50;
+ }
+ }
+ return super.act();
+ }
+
+ @Override
+ public void damage(int dmg, Object src) {
+ if (dmg >= 25){
+ //takes 25/26/27/28/29/30/31/32/33/34/35 dmg
+ // at 25/27/30/34/39/45/52/60/69/79/90 incoming dmg
+ dmg = 24 + (int)(Math.sqrt(8*(dmg - 24) + 1) - 1)/2;
+ }
+ spawnCooldown -= dmg;
+ super.damage(dmg, src);
+ }
+}
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java
index aa0548700..e16d7cf7a 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java
@@ -52,7 +52,7 @@ public class Eye extends Mob {
viewDistance = Light.DISTANCE;
EXP = 13;
- maxLvl = 25;
+ maxLvl = 26;
flying = true;
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
new file mode 100644
index 000000000..e362751f7
--- /dev/null
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java
@@ -0,0 +1,66 @@
+/*
+ * Pixel Dungeon
+ * Copyright (C) 2012-2015 Oleg Dolya
+ *
+ * Shattered Pixel Dungeon
+ * Copyright (C) 2014-2019 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.mobs;
+
+import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
+import com.shatteredpixel.shatteredpixeldungeon.sprites.RipperSprite;
+import com.watabou.utils.Random;
+
+public class RipperDemon extends Mob {
+
+ {
+ spriteClass = RipperSprite.class;
+
+ HP = HT = 60;
+ defenseSkill = 22;
+
+ EXP = 9; //for corrupting
+ maxLvl = -2;
+
+ baseSpeed = 2f;
+ }
+
+ @Override
+ public float spawningWeight() {
+ return 0;
+ }
+
+ @Override
+ public int damageRoll() {
+ return Random.NormalIntRange( 20, 25 );
+ }
+
+ @Override
+ public int attackSkill( Char target ) {
+ return 35;
+ }
+
+ @Override
+ protected float attackDelay() {
+ return super.attackDelay()*0.5f;
+ }
+
+ @Override
+ public int drRoll() {
+ return Random.NormalIntRange(0, 4);
+ }
+}
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java
index 3e6eeb756..414df0cd5 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java
@@ -45,7 +45,7 @@ public class Scorpio extends Mob {
viewDistance = Light.DISTANCE;
EXP = 14;
- maxLvl = 25;
+ maxLvl = 27;
loot = Generator.Category.POTION;
lootChance = 0.5f;
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java
index bfc012ced..b60dfaf47 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java
@@ -26,6 +26,8 @@ import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.Torch;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.HallsPainter;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
+import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
+import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.DemonSpawnerRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.BlazingTrap;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.CorrosionTrap;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.CursingTrap;
@@ -51,6 +53,8 @@ import com.watabou.noosa.particles.PixelParticle;
import com.watabou.utils.PointF;
import com.watabou.utils.Random;
+import java.util.ArrayList;
+
public class HallsLevel extends RegularLevel {
{
@@ -60,6 +64,18 @@ public class HallsLevel extends RegularLevel {
color1 = 0x801500;
color2 = 0xa68521;
}
+
+ @Override
+ protected ArrayList initRooms() {
+ ArrayList rooms = super.initRooms();
+
+ rooms.add(new DemonSpawnerRoom());
+ if (Dungeon.depth == 24){
+ rooms.add(new DemonSpawnerRoom());
+ }
+
+ return rooms;
+ }
@Override
protected int standardRooms() {
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/DemonSpawnerRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/DemonSpawnerRoom.java
new file mode 100644
index 000000000..49e8d24bb
--- /dev/null
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/DemonSpawnerRoom.java
@@ -0,0 +1,74 @@
+/*
+ * Pixel Dungeon
+ * Copyright (C) 2012-2015 Oleg Dolya
+ *
+ * Shattered Pixel Dungeon
+ * Copyright (C) 2014-2019 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.actors.mobs.DemonSpawner;
+import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RipperDemon;
+import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
+import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
+import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
+import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
+import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.EntranceRoom;
+import com.watabou.utils.Point;
+import com.watabou.utils.Random;
+
+public class DemonSpawnerRoom extends SpecialRoom {
+ @Override
+ public void paint(Level level) {
+
+ Painter.fill( level, this, Terrain.WALL );
+ Painter.fill( level, this, 1, Terrain.EMPTY );
+
+ Point c = center();
+ int cx = c.x;
+ int cy = c.y;
+
+ Door door = entrance();
+ door.set(Door.Type.UNLOCKED);
+
+ DemonSpawner spawner = new DemonSpawner();
+ spawner.pos = cx + cy * level.width();
+ level.mobs.add( spawner );
+
+ int rippers = Random.IntRange(1, 2);
+
+ for (int i = 0; i < rippers; i++){
+ int pos;
+ do {
+ pos = level.pointToCell(random(1));
+ } while (level.solid[pos] || level.findMob(pos) != null);
+
+ RipperDemon ripper = new RipperDemon();
+ ripper.pos = pos;
+ ripper.state = ripper.HUNTING;
+ level.mobs.add( ripper );
+ }
+
+ }
+
+ @Override
+ public boolean connect(Room room) {
+ //cannot connect to entrance, otherwise works normally
+ if (room instanceof EntranceRoom) return false;
+ else return super.connect(room);
+ }
+}
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/RipperSprite.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/RipperSprite.java
new file mode 100644
index 000000000..7e6a8a5e0
--- /dev/null
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/RipperSprite.java
@@ -0,0 +1,55 @@
+/*
+ * Pixel Dungeon
+ * Copyright (C) 2012-2015 Oleg Dolya
+ *
+ * Shattered Pixel Dungeon
+ * Copyright (C) 2014-2019 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.sprites;
+
+import com.shatteredpixel.shatteredpixeldungeon.Assets;
+import com.watabou.noosa.TextureFilm;
+
+//TODO currently just a reskinned warlock sprite
+public class RipperSprite extends MobSprite {
+
+ public RipperSprite() {
+ super();
+
+ texture( Assets.RIPPER );
+
+ TextureFilm frames = new TextureFilm( texture, 12, 15 );
+
+ idle = new Animation( 2, true );
+ idle.frames( frames, 0, 0, 0, 1, 0, 0, 1, 1 );
+
+ run = new Animation( 15, true );
+ run.frames( frames, 0, 2, 3, 4 );
+
+ //TODO shoudl probably have 2 attack animations, like monks
+ attack = new Animation( 12, false );
+ attack.frames( frames, 0, 5, 6 );
+
+ zap = attack.clone();
+
+ die = new Animation( 15, false );
+ die.frames( frames, 0, 7, 8, 8, 9, 10 );
+
+ play( idle );
+ }
+
+}
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/SpawnerSprite.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/SpawnerSprite.java
new file mode 100644
index 000000000..80163e884
--- /dev/null
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/SpawnerSprite.java
@@ -0,0 +1,52 @@
+/*
+ * Pixel Dungeon
+ * Copyright (C) 2012-2015 Oleg Dolya
+ *
+ * Shattered Pixel Dungeon
+ * Copyright (C) 2014-2019 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.sprites;
+
+import com.shatteredpixel.shatteredpixeldungeon.Assets;
+import com.watabou.noosa.TextureFilm;
+
+public class SpawnerSprite extends MobSprite {
+
+ //TODO just a recolored golem sprite atm
+ public SpawnerSprite() {
+ super();
+
+ texture( Assets.SPAWNER );
+
+ TextureFilm frames = new TextureFilm( texture, 16, 16 );
+
+ idle = new Animation( 4, true );
+ idle.frames( frames, 0, 1 );
+
+ run = new Animation( 12, true );
+ run.frames( frames, 2, 3, 4, 5 );
+
+ attack = new Animation( 10, false );
+ attack.frames( frames, 6, 7, 8 );
+
+ die = new Animation( 15, false );
+ die.frames( frames, 9, 10, 11, 12, 13 );
+
+ play( idle );
+ }
+
+}
diff --git a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties
index 66915086f..2198175b9 100644
--- a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties
+++ b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties
@@ -470,6 +470,9 @@ actors.mobs.causticslime.desc=This slime seems to have been tainted by the dark
actors.mobs.crab.name=sewer crab
actors.mobs.crab.desc=These huge crabs are at the top of the food chain in the sewers. They are extremely fast and their thick carapace can withstand heavy blows.
+actors.mobs.demonspawner.name=demon spawner
+actors.mobs.demonspawner.desc=This twisting amalgam of dwarven flesh is responsible for creating ripper demons from dwarven body parts. Clearly the demons see no issue with using every resource they can against their enemies.\n\nWhile visually terrifying, demon spawners have no means of moving or directly defending themselves. Their considerable mass makes them hard to kill quickly however, and they will spawn ripper demons more quickly when they are under threat.
+
actors.mobs.dm100.name=DM-100
actors.mobs.dm100.zap_kill=The lightning bolt killed you...
actors.mobs.dm100.desc=The DM-100 is an early model of dwarven 'defense machine' which was designed to protect dwarven miners in the caves below. Their electrical shocks proved too weak however, and so they were gifted to the human prison above. The warden initially deemed them too cruel to use, but as the prisoners became more unruly they became a necessity.
@@ -572,6 +575,9 @@ actors.mobs.piranha.desc=These carnivorous fish are not natural inhabitants of u
actors.mobs.rat.name=marsupial rat
actors.mobs.rat.desc=Marsupial rats are aggressive but rather weak denizens of the sewers. They have a nasty bite, but are only life threatening in large numbers.
+actors.mobs.ripperdemon.name=ripper demon
+actors.mobs.ripperdemon.desc=These horrific creatures are the result of the many dwarven corpses in this region being put to use by demonic forces. Rippers are emaciated ghoulish creatures that resemble dwarves, but with broken bodies and long sharp claws made of bone.\n\nRipper demons are not particularly durable, but they are fast and dangerous. Killing them individually is not difficult, but they just seem to keep coming...
+
actors.mobs.rotheart.name=rot heart
actors.mobs.rotheart.desc=A Rotberry's fruit is very unique. Instead of rotting away and providing nutrients, the fruit grows, hardens, and encompasses the seed. It provides protection for the internal organs which grow inside the fruit. This giant orb is referred to as the heart of an adult rotberry plant.