v2.2.0: overhauled the wandmaker's rotberry quest
This commit is contained in:
@@ -1247,7 +1247,7 @@ 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.
|
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.
|
||||||
|
|
||||||
actors.mobs.rotlasher.name=rot lasher
|
actors.mobs.rotlasher.name=rot lasher
|
||||||
actors.mobs.rotlasher.desc=The rot lasher is a part of a mature rotberry plant's root structure, and also their primary means of defense. Lashers are stuck into the ground, but will violently assault any threat that gets near to them. When there is no nearby enemies, they stand motionless, attempting to blend in with surrounding vegetation.
|
actors.mobs.rotlasher.desc=The rot lasher is a part of a mature rotberry plant's root structure, and also their primary means of defense. Lashers are blind and stuck into the ground, but will violently assault any nearby threat after taking a moment to detect them. When there is no nearby enemies, they stand motionless, attempting to blend in with surrounding vegetation.
|
||||||
actors.mobs.rotlasher$waiting.status=This %s is idle.
|
actors.mobs.rotlasher$waiting.status=This %s is idle.
|
||||||
|
|
||||||
actors.mobs.scorpio.name=scorpio
|
actors.mobs.scorpio.name=scorpio
|
||||||
|
|||||||
+10
-1
@@ -36,6 +36,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
|
|||||||
import com.shatteredpixel.shatteredpixeldungeon.plants.Rotberry;
|
import com.shatteredpixel.shatteredpixeldungeon.plants.Rotberry;
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.RotHeartSprite;
|
import com.shatteredpixel.shatteredpixeldungeon.sprites.RotHeartSprite;
|
||||||
|
import com.watabou.utils.PathFinder;
|
||||||
import com.watabou.utils.Random;
|
import com.watabou.utils.Random;
|
||||||
|
|
||||||
public class RotHeart extends Mob {
|
public class RotHeart extends Mob {
|
||||||
@@ -73,7 +74,15 @@ public class RotHeart extends Mob {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int defenseProc(Char enemy, int damage) {
|
public int defenseProc(Char enemy, int damage) {
|
||||||
GameScene.add(Blob.seed(pos, 20, ToxicGas.class));
|
//rot heart spreads less gas in enclosed spaces
|
||||||
|
int openNearby = 2;
|
||||||
|
for (int i : PathFinder.NEIGHBOURS8){
|
||||||
|
if (!Dungeon.level.solid[pos+i]){
|
||||||
|
openNearby++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameScene.add(Blob.seed(pos, 2*openNearby, ToxicGas.class));
|
||||||
|
|
||||||
return super.defenseProc(enemy, damage);
|
return super.defenseProc(enemy, damage);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-8
@@ -36,15 +36,16 @@ public class RotLasher extends Mob {
|
|||||||
{
|
{
|
||||||
spriteClass = RotLasherSprite.class;
|
spriteClass = RotLasherSprite.class;
|
||||||
|
|
||||||
HP = HT = 40;
|
HP = HT = 80;
|
||||||
defenseSkill = 0;
|
defenseSkill = 0;
|
||||||
|
|
||||||
EXP = 1;
|
EXP = 1;
|
||||||
|
|
||||||
loot = Generator.Category.SEED;
|
loot = Generator.Category.SEED;
|
||||||
lootChance = 1f;
|
lootChance = 0.75f;
|
||||||
|
|
||||||
state = WANDERING = new Waiting();
|
state = WANDERING = new Waiting();
|
||||||
|
viewDistance = 1;
|
||||||
|
|
||||||
properties.add(Property.IMMOVABLE);
|
properties.add(Property.IMMOVABLE);
|
||||||
properties.add(Property.MINIBOSS);
|
properties.add(Property.MINIBOSS);
|
||||||
@@ -53,7 +54,7 @@ public class RotLasher extends Mob {
|
|||||||
@Override
|
@Override
|
||||||
protected boolean act() {
|
protected boolean act() {
|
||||||
if (enemy == null || !Dungeon.level.adjacent(pos, enemy.pos)) {
|
if (enemy == null || !Dungeon.level.adjacent(pos, enemy.pos)) {
|
||||||
HP = Math.min(HT, HP + 3);
|
HP = Math.min(HT, HP + 5);
|
||||||
}
|
}
|
||||||
return super.act();
|
return super.act();
|
||||||
}
|
}
|
||||||
@@ -82,22 +83,22 @@ public class RotLasher extends Mob {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean getCloser(int target) {
|
protected boolean getCloser(int target) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean getFurther(int target) {
|
protected boolean getFurther(int target) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int damageRoll() {
|
public int damageRoll() {
|
||||||
return Random.NormalIntRange(8, 15);
|
return Random.NormalIntRange(10, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int attackSkill( Char target ) {
|
public int attackSkill( Char target ) {
|
||||||
return 15;
|
return 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,5 +110,12 @@ public class RotLasher extends Mob {
|
|||||||
immunities.add( ToxicGas.class );
|
immunities.add( ToxicGas.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Waiting extends Mob.Wandering{}
|
private class Waiting extends Mob.Wandering{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean noticeEnemy() {
|
||||||
|
spend(TICK);
|
||||||
|
return super.noticeEnemy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+108
-28
@@ -22,6 +22,7 @@
|
|||||||
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special;
|
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special;
|
||||||
|
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
||||||
|
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
|
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RotHeart;
|
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RotHeart;
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RotLasher;
|
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RotLasher;
|
||||||
@@ -30,15 +31,20 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
|
|||||||
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
|
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
|
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
|
||||||
import com.watabou.utils.PathFinder;
|
import com.watabou.utils.PathFinder;
|
||||||
|
import com.watabou.utils.Point;
|
||||||
import com.watabou.utils.Random;
|
import com.watabou.utils.Random;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class RotGardenRoom extends SpecialRoom {
|
public class RotGardenRoom extends SpecialRoom {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int minWidth() { return 7; }
|
public int minWidth() { return 10; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int minHeight() { return 7; }
|
public int minHeight() { return 10; }
|
||||||
|
|
||||||
public void paint( Level level ) {
|
public void paint( Level level ) {
|
||||||
|
|
||||||
@@ -46,38 +52,103 @@ public class RotGardenRoom extends SpecialRoom {
|
|||||||
entrance.set(Door.Type.LOCKED);
|
entrance.set(Door.Type.LOCKED);
|
||||||
level.addItemToSpawn(new IronKey(Dungeon.depth));
|
level.addItemToSpawn(new IronKey(Dungeon.depth));
|
||||||
|
|
||||||
|
//define basic terrain, mostly high grass with some chaotically placed wall tiles
|
||||||
Painter.fill(level, this, Terrain.WALL);
|
Painter.fill(level, this, Terrain.WALL);
|
||||||
Painter.fill(level, this, 1, Terrain.GRASS);
|
Painter.set(level, entrance, Terrain.LOCKED_DOOR);
|
||||||
|
Painter.fill(level, this, 1, Terrain.HIGH_GRASS);
|
||||||
|
for (int i = 0; i < 12; i ++){
|
||||||
|
Painter.set(level, random(1), Terrain.WALL);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 8; i ++){
|
||||||
|
Painter.set(level, random(2), Terrain.WALL);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; i ++){
|
||||||
|
Painter.set(level, random(3), Terrain.WALL);
|
||||||
|
}
|
||||||
|
Painter.drawInside(level, this, entrance, 3, Terrain.HIGH_GRASS);
|
||||||
|
|
||||||
|
boolean[] passable = new boolean[level.length()];
|
||||||
int heartX = Random.IntRange(left+1, right-1);
|
for (int i = 0; i < passable.length; i++){
|
||||||
int heartY = Random.IntRange(top+1, bottom-1);
|
passable[i] = level.map[i] != Terrain.WALL;
|
||||||
|
|
||||||
if (entrance.x == left) {
|
|
||||||
heartX = right - 1;
|
|
||||||
} else if (entrance.x == right) {
|
|
||||||
heartX = left + 1;
|
|
||||||
} else if (entrance.y == top) {
|
|
||||||
heartY = bottom - 1;
|
|
||||||
} else if (entrance.y == bottom) {
|
|
||||||
heartY = top + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
placePlant(level, heartX + heartY * level.width(), new RotHeart());
|
//place the heart in a slightly random location sufficiently far from the entrance
|
||||||
|
int entryPos = level.pointToCell(entrance());
|
||||||
|
PathFinder.buildDistanceMap(entryPos, passable);
|
||||||
|
ArrayList<Integer> candidates = new ArrayList<>();
|
||||||
|
for (Point p : getPoints()){
|
||||||
|
int i = level.pointToCell(p);
|
||||||
|
if (PathFinder.distance[i] != Integer.MAX_VALUE){
|
||||||
|
if (PathFinder.distance[i] >= 3){
|
||||||
|
candidates.add(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//fill in grass tiles that are enclosed
|
||||||
|
if (level.map[i] == Terrain.HIGH_GRASS){
|
||||||
|
level.map[i] = Terrain.WALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Random.shuffle(candidates);
|
||||||
|
int closestPos = 3;
|
||||||
|
while (candidates.size() > 5){
|
||||||
|
for (Integer i : candidates.toArray(new Integer[0])){
|
||||||
|
if (candidates.size() > 5 && PathFinder.distance[i] == closestPos){
|
||||||
|
candidates.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closestPos++;
|
||||||
|
}
|
||||||
|
int heartPos = Random.element(candidates);
|
||||||
|
placePlant(level, heartPos, new RotHeart());
|
||||||
|
|
||||||
int lashers = ((width()-2)*(height()-2))/8;
|
//place up to 6 lashers in such a way that there is always a safe path to the heart
|
||||||
|
boolean[] newPassable = Arrays.copyOf(passable, passable.length);
|
||||||
for (int i = 1; i <= lashers; i++){
|
int maxLashers = 6;
|
||||||
|
for (int i = 1; i <= maxLashers; i++){
|
||||||
int pos;
|
int pos;
|
||||||
|
int tries = 50;
|
||||||
do {
|
do {
|
||||||
pos = level.pointToCell(random());
|
pos = level.pointToCell(random());
|
||||||
} while (!validPlantPos(level, pos));
|
tries--;
|
||||||
|
} while (tries > 0 && !validPlantPos(passable, newPassable, level, pos, heartPos, entryPos));
|
||||||
|
if (tries <= 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
placePlant(level, pos, new RotLasher());
|
placePlant(level, pos, new RotLasher());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if almost every open cell next to the heart has a lasher threatening it, try to clear one lasher
|
||||||
|
int safeHeartcells = 0;
|
||||||
|
HashSet<Mob> adjacentLashers = new HashSet<>();
|
||||||
|
for (int i : PathFinder.NEIGHBOURS8){
|
||||||
|
if (level.map[heartPos+i] == Terrain.WALL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean foundLasher = false;
|
||||||
|
for (int j : PathFinder.NEIGHBOURS8){
|
||||||
|
if (heartPos+i+j != heartPos
|
||||||
|
&& level.map[heartPos+i+j] != Terrain.WALL
|
||||||
|
&& level.findMob(heartPos+i+j) != null){
|
||||||
|
foundLasher = true;
|
||||||
|
adjacentLashers.add(level.findMob(heartPos+i+j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundLasher){
|
||||||
|
safeHeartcells++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (safeHeartcells < 2 && !adjacentLashers.isEmpty()){
|
||||||
|
Char toRemove = Random.element(adjacentLashers);
|
||||||
|
level.mobs.remove(toRemove);
|
||||||
|
Painter.set(level, toRemove.pos, Terrain.HIGH_GRASS);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean validPlantPos(Level level, int pos){
|
private static boolean validPlantPos(boolean[] passable, boolean[] newPassable, Level level, int pos, int heartPos, int entryPos){
|
||||||
if (level.map[pos] != Terrain.GRASS){
|
if (level.map[pos] != Terrain.HIGH_GRASS){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,17 +158,26 @@ public class RotGardenRoom extends SpecialRoom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
newPassable[pos] = false;
|
||||||
|
for (int i : PathFinder.NEIGHBOURS4){
|
||||||
|
newPassable[pos+i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathFinder.buildDistanceMap(heartPos, newPassable);
|
||||||
|
|
||||||
|
if (PathFinder.distance[entryPos] == Integer.MAX_VALUE){
|
||||||
|
System.arraycopy(passable, 0, newPassable, 0, passable.length);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
System.arraycopy(newPassable, 0, passable, 0, passable.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void placePlant(Level level, int pos, Mob plant){
|
private static void placePlant(Level level, int pos, Mob plant){
|
||||||
plant.pos = pos;
|
plant.pos = pos;
|
||||||
level.mobs.add( plant );
|
level.mobs.add( plant );
|
||||||
|
|
||||||
for(int i : PathFinder.NEIGHBOURS8) {
|
Painter.set(level, pos, Terrain.GRASS);
|
||||||
if (level.map[pos + i] == Terrain.GRASS){
|
|
||||||
Painter.set(level, pos + i, Terrain.HIGH_GRASS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user