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.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.scorpio.name=scorpio
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.plants.Rotberry;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.RotHeartSprite;
|
||||
import com.watabou.utils.PathFinder;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
public class RotHeart extends Mob {
|
||||
@@ -73,7 +74,15 @@ public class RotHeart extends Mob {
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -36,15 +36,16 @@ public class RotLasher extends Mob {
|
||||
{
|
||||
spriteClass = RotLasherSprite.class;
|
||||
|
||||
HP = HT = 40;
|
||||
HP = HT = 80;
|
||||
defenseSkill = 0;
|
||||
|
||||
EXP = 1;
|
||||
|
||||
loot = Generator.Category.SEED;
|
||||
lootChance = 1f;
|
||||
lootChance = 0.75f;
|
||||
|
||||
state = WANDERING = new Waiting();
|
||||
viewDistance = 1;
|
||||
|
||||
properties.add(Property.IMMOVABLE);
|
||||
properties.add(Property.MINIBOSS);
|
||||
@@ -53,7 +54,7 @@ public class RotLasher extends Mob {
|
||||
@Override
|
||||
protected boolean act() {
|
||||
if (enemy == null || !Dungeon.level.adjacent(pos, enemy.pos)) {
|
||||
HP = Math.min(HT, HP + 3);
|
||||
HP = Math.min(HT, HP + 5);
|
||||
}
|
||||
return super.act();
|
||||
}
|
||||
@@ -82,22 +83,22 @@ public class RotLasher extends Mob {
|
||||
|
||||
@Override
|
||||
protected boolean getCloser(int target) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getFurther(int target) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int damageRoll() {
|
||||
return Random.NormalIntRange(8, 15);
|
||||
return Random.NormalIntRange(10, 20);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int attackSkill( Char target ) {
|
||||
return 15;
|
||||
return 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,5 +110,12 @@ public class RotLasher extends Mob {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RotHeart;
|
||||
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.painters.Painter;
|
||||
import com.watabou.utils.PathFinder;
|
||||
import com.watabou.utils.Point;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class RotGardenRoom extends SpecialRoom {
|
||||
|
||||
@Override
|
||||
public int minWidth() { return 7; }
|
||||
public int minWidth() { return 10; }
|
||||
|
||||
@Override
|
||||
public int minHeight() { return 7; }
|
||||
public int minHeight() { return 10; }
|
||||
|
||||
public void paint( Level level ) {
|
||||
|
||||
@@ -46,38 +52,103 @@ public class RotGardenRoom extends SpecialRoom {
|
||||
entrance.set(Door.Type.LOCKED);
|
||||
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, 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);
|
||||
|
||||
|
||||
int heartX = Random.IntRange(left+1, right-1);
|
||||
int heartY = Random.IntRange(top+1, bottom-1);
|
||||
|
||||
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;
|
||||
boolean[] passable = new boolean[level.length()];
|
||||
for (int i = 0; i < passable.length; i++){
|
||||
passable[i] = level.map[i] != Terrain.WALL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (int i = 1; i <= lashers; i++){
|
||||
//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);
|
||||
int maxLashers = 6;
|
||||
for (int i = 1; i <= maxLashers; i++){
|
||||
int pos;
|
||||
int tries = 50;
|
||||
do {
|
||||
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());
|
||||
}
|
||||
|
||||
//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){
|
||||
if (level.map[pos] != Terrain.GRASS){
|
||||
private static boolean validPlantPos(boolean[] passable, boolean[] newPassable, Level level, int pos, int heartPos, int entryPos){
|
||||
if (level.map[pos] != Terrain.HIGH_GRASS){
|
||||
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){
|
||||
plant.pos = pos;
|
||||
level.mobs.add( plant );
|
||||
|
||||
for(int i : PathFinder.NEIGHBOURS8) {
|
||||
if (level.map[pos + i] == Terrain.GRASS){
|
||||
Painter.set(level, pos + i, Terrain.HIGH_GRASS);
|
||||
}
|
||||
}
|
||||
Painter.set(level, pos, Terrain.GRASS);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user