v2.1.0: added a new exotic enemy: phantom piranhas (some text missing)

This commit is contained in:
Evan Debenham
2023-04-28 16:59:13 -04:00
parent 1c7a7b6a96
commit b3fb41175c
15 changed files with 316 additions and 9 deletions

View File

@@ -1197,6 +1197,10 @@ actors.mobs.monk.desc=These monks are fanatics, who have devoted themselves to p
actors.mobs.monk$focus.name=focused
actors.mobs.monk$focus.desc=This monk is perfectly honed in on their target, and seem to be anticipating their moves before they make them.\n\nWhile focused, the next physical attack made against this character is guaranteed to miss, no matter what circumstances there are. Parrying this attack will spend the monk's focus, and they will need to build it up again to parry another attack. Monks build focus more quickly while they are moving.
actors.mobs.phantompiranha.name=phantom piranha
actors.mobs.phantompiranha.teleport_away=The phantom piranha teleports away...
actors.mobs.phantompiranha.desc=TODO
actors.mobs.piranha.name=giant piranha
actors.mobs.piranha.desc=These carnivorous fish are not natural inhabitants of underground pools. They were bred specifically to protect flooded treasure vaults.

View File

@@ -568,6 +568,9 @@ items.food.mysterymeat.stuffed=You are stuffed.
items.food.mysterymeat.desc=Eat at your own risk!
items.food.mysterymeat$placeholder.name=meat
items.food.phantommeat.name=phantom meat
items.food.phantommeat.desc=TODO
items.food.pasty.pasty=pasty
items.food.pasty.pie=pumpkin pie
items.food.pasty.cane=candy cane

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 B

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,112 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2023 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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.food.PhantomMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.PhantomPiranhaSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
import java.util.ArrayList;
public class PhantomPiranha extends Piranha {
{
spriteClass = PhantomPiranhaSprite.class;
loot = PhantomMeat.class;
lootChance = 1f;
}
@Override
public void damage(int dmg, Object src) {
Char dmgSource = null;
if (src instanceof Char) dmgSource = (Char)src;
if (src instanceof Wand) dmgSource = Dungeon.hero;
if (dmgSource == null || !Dungeon.level.adjacent(pos, dmgSource.pos)){
dmg = Math.round(dmg/2f); //halve damage taken if we are going to teleport
}
super.damage(dmg, src);
if (isAlive()) {
if (dmgSource != null) {
if (!Dungeon.level.adjacent(pos, dmgSource.pos)) {
ArrayList<Integer> candidates = new ArrayList<>();
for (int i : PathFinder.NEIGHBOURS8) {
if (Dungeon.level.water[dmgSource.pos + i] && Actor.findChar(dmgSource.pos + i) == null) {
candidates.add(dmgSource.pos + i);
}
}
if (!candidates.isEmpty()) {
ScrollOfTeleportation.appear(this, Random.element(candidates));
aggro(dmgSource);
} else {
teleportAway();
}
}
} else {
teleportAway();
}
}
}
@Override
public int defenseProc(Char enemy, int damage) {
return super.defenseProc(enemy, damage);
}
@Override
public void dieOnLand() {
teleportAway();
}
private void teleportAway(){
ArrayList<Integer> inFOVCandidates = new ArrayList<>();
ArrayList<Integer> outFOVCandidates = new ArrayList<>();
for (int i = 0; i < Dungeon.level.length(); i++){
if (Dungeon.level.water[i] && Actor.findChar(i) == null){
if (Dungeon.level.heroFOV[i]){
inFOVCandidates.add(i);
} else {
outFOVCandidates.add(i);
}
}
}
if (!outFOVCandidates.isEmpty()){
ScrollOfTeleportation.appear(this, Random.element(outFOVCandidates));
GLog.i(Messages.get(this, "teleport_away"));
} else if (!inFOVCandidates.isEmpty()){
ScrollOfTeleportation.appear(this, Random.element(inFOVCandidates));
}
}
}

View File

@@ -66,7 +66,7 @@ public class Piranha extends Mob {
protected boolean act() {
if (!Dungeon.level.water[pos]) {
die( null );
dieOnLand();
return true;
} else {
return super.act();
@@ -99,7 +99,11 @@ public class Piranha extends Mob {
}
return super.surprisedBy(enemy, attacking);
}
public void dieOnLand(){
die( null );
}
@Override
public void die( Object cause ) {
super.die( cause );
@@ -191,4 +195,12 @@ public class Piranha extends Mob {
return super.act(enemyInFOV, justAlerted);
}
}
public static Piranha random(){
if (Random.Int(50) == 0){
return new PhantomPiranha();
} else {
return new Piranha();
}
}
}

View File

@@ -39,6 +39,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.food.Blandfruit;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Food;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MeatPie;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Pasty;
import com.shatteredpixel.shatteredpixeldungeon.items.food.PhantomMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfEnergy;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
@@ -217,8 +218,8 @@ public class HornOfPlenty extends Artifact {
if (level() >= 10) return;
storedFoodEnergy += food.energy;
//Pasties are worth two upgrades instead of 1.5, meat pies are worth 4 instead of 3!
if (food instanceof Pasty){
//Pasties and phantom meat are worth two upgrades instead of 1.5, meat pies are worth 4 instead of 3!
if (food instanceof Pasty || food instanceof PhantomMeat){
storedFoodEnergy += Hunger.HUNGRY/2;
} else if (food instanceof MeatPie){
storedFoodEnergy += Hunger.HUNGRY;

View File

@@ -58,7 +58,7 @@ public class MeatPie extends Food {
for (Item ingredient : ingredients){
if (ingredient.quantity() > 0) {
if (ingredient instanceof Pasty) {
if (ingredient instanceof Pasty || ingredient instanceof PhantomMeat) {
pasty = true;
} else if (ingredient.getClass() == Food.class) {
ration = true;

View File

@@ -0,0 +1,63 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2023 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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.items.food;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barkskin;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hunger;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
public class PhantomMeat extends Food {
{
image = ItemSpriteSheet.PHANTOM_MEAT;
energy = Hunger.STARVING;
}
@Override
protected void satisfy(Hero hero) {
super.satisfy(hero);
effect(hero);
}
public int value() {
return 30 * quantity;
}
public static void effect(Hero hero){
Buff.affect( hero, Barkskin.class ).set( hero.HT / 4, 1 );
Buff.affect( hero, Invisibility.class, Invisibility.DURATION );
if (hero.HP < hero.HT) {
hero.HP = Math.min( hero.HP + hero.HT / 4, hero.HT );
}
hero.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 );
PotionOfHealing.cure(hero);
}
}

View File

@@ -1081,7 +1081,7 @@ public abstract class Level implements Bundlable {
}
if (ch.isAlive() && ch instanceof Piranha && !water[ch.pos]){
ch.die(null);
((Piranha) ch).dieOnLand();
}
}

View File

@@ -90,7 +90,7 @@ public class PoolRoom extends SpecialRoom {
level.addItemToSpawn( new PotionOfInvisibility() );
for (int i=0; i < NPIRANHAS; i++) {
Piranha piranha = new Piranha();
Piranha piranha = Piranha.random();
do {
piranha.pos = level.pointToCell(random());
} while (level.map[piranha.pos] != Terrain.WATER|| level.findMob( piranha.pos ) != null);

View File

@@ -65,7 +65,7 @@ public class AquariumRoom extends StandardRoom {
int numFish = (minDim - 4)/3; //1-3 fish, depending on room size
for (int i=0; i < numFish; i++) {
Piranha piranha = new Piranha();
Piranha piranha = Piranha.random();
do {
piranha.pos = level.pointToCell(random(3));
} while (level.map[piranha.pos] != Terrain.WATER|| level.findMob( piranha.pos ) != null);

View File

@@ -120,7 +120,7 @@ public class DistortionTrap extends Trap{
continue; //wraiths spawn themselves, no need to do more
case 1:
//yes it's intended that these are likely to die right away
mob = new Piranha();
mob = Piranha.random();
break;
case 2:
mob = Mimic.spawnAt(point, new ArrayList<>());

View File

@@ -645,6 +645,7 @@ public class ItemSpriteSheet {
public static final int BLANDFRUIT = FOOD+10;
public static final int BLAND_CHUNKS= FOOD+11;
public static final int BERRY = FOOD+12;
public static final int PHANTOM_MEAT= FOOD+13;
static{
assignItemRect(MEAT, 15, 11);
assignItemRect(STEAK, 15, 11);
@@ -659,6 +660,7 @@ public class ItemSpriteSheet {
assignItemRect(BLANDFRUIT, 9, 12);
assignItemRect(BLAND_CHUNKS,14, 6);
assignItemRect(BERRY, 9, 11);
assignItemRect(PHANTOM_MEAT,15, 11);
}
private static final int QUEST = xy(1, 29); //32 slots

View File

@@ -0,0 +1,110 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2023 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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.watabou.noosa.MovieClip;
import com.watabou.noosa.TextureFilm;
import com.watabou.noosa.particles.Emitter;
public class PhantomPiranhaSprite extends MobSprite {
private Emitter sparkles;
public PhantomPiranhaSprite() {
super();
renderShadow = false;
perspectiveRaise = 0.2f;
texture( Assets.Sprites.PIRANHA );
TextureFilm frames = new TextureFilm( texture, 12, 16 );
int c = 21;
idle = new MovieClip.Animation( 8, true );
idle.frames( frames, c+0, c+1, c+2, c+1 );
run = new MovieClip.Animation( 20, true );
run.frames( frames, c+0, c+1, c+2, c+1 );
attack = new MovieClip.Animation( 20, false );
attack.frames( frames, c+3, c+4, c+5, c+6, c+7, c+8, c+9, c+10, c+11 );
die = new MovieClip.Animation( 4, false );
die.frames( frames, c+12, c+13, c+14 );
play( idle );
}
@Override
public void link(Char ch) {
super.link(ch);
renderShadow = false;
if (sparkles == null) {
sparkles = emitter();
sparkles.pour( Speck.factory( Speck.LIGHT ), 0.5f );
}
}
@Override
public void update() {
super.update();
if (sparkles != null) {
sparkles.visible = visible;
}
}
@Override
public void die() {
super.die();
if (sparkles != null) {
sparkles.on = false;
}
}
@Override
public void kill() {
super.kill();
if (sparkles != null) {
sparkles.on = false;
}
}
@Override
public void onComplete( MovieClip.Animation anim ) {
super.onComplete( anim );
if (anim == attack) {
GameScene.ripple( ch.pos );
}
}
}