v2.2.0: overhauled the wandmaker's rotberry quest

This commit is contained in:
Evan Debenham
2023-07-25 12:48:50 -04:00
parent e5e3503cfa
commit 40da2d003c
4 changed files with 135 additions and 38 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}