From e047d1aee1a618aa21ac82ec89476229ca711a50 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Thu, 13 Jun 2019 19:06:23 -0400 Subject: [PATCH] v0.7.4: implemented directing functionality for the ghost hero --- .../items/artifacts/DriedRose.java | 117 ++++++++++++++++-- .../messages/items/items.properties | 5 +- 2 files changed, 107 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java index 5dc5bb820..cdae29c0a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java @@ -44,9 +44,9 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.AntiMagic; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRetribution; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfPsionicBlast; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon; -import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.messages.Languages; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.CellSelector; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.GhostSprite; @@ -102,9 +102,12 @@ public class DriedRose extends Artifact { actions.remove(AC_EQUIP); return actions; } - if (isEquipped( hero ) && charge == chargeCap && !cursed) { + if (isEquipped( hero ) && charge == chargeCap && !cursed && ghostID != 0) { actions.add(AC_SUMMON); } + if (ghostID != 0){ + actions.add(AC_DIRECT); + } if (isIdentified() && !cursed){ actions.add(AC_OUTFIT); } @@ -160,6 +163,17 @@ public class DriedRose extends Artifact { GLog.i( Messages.get(this, "no_space") ); } + } else if (action.equals(AC_DIRECT)){ + if (ghost == null && ghostID != 0){ + Actor a = Actor.findById(ghostID); + if (a != null){ + ghost = (GhostHero)a; + } else { + ghostID = 0; + } + } + if (ghost != null) GameScene.selectCell(ghostDirector); + } else if (action.equals(AC_OUTFIT)){ GameScene.show( new WndGhostHero(this) ); } @@ -258,6 +272,8 @@ public class DriedRose extends Artifact { ghostID = bundle.getInt( GHOSTID ); droppedPetals = bundle.getInt( PETALS ); + if (ghostID != 0) defaultAction = AC_DIRECT; + if (bundle.contains(WEAPON)) weapon = (MeleeWeapon)bundle.get( WEAPON ); if (bundle.contains(ARMOR)) armor = (Armor)bundle.get( ARMOR ); } @@ -280,7 +296,10 @@ public class DriedRose extends Artifact { //rose does not charge while ghost hero is alive if (ghost != null){ + defaultAction = AC_DIRECT; return true; + } else { + defaultAction = AC_SUMMON; } LockedFloor lock = target.buff(LockedFloor.class); @@ -317,6 +336,47 @@ public class DriedRose extends Artifact { return true; } } + + //FIXME need to translate phrases here + public CellSelector.Listener ghostDirector = new CellSelector.Listener(){ + + @Override + public void onSelect(Integer cell) { + if (cell == null) return; + + if (!Dungeon.level.heroFOV[cell] || Actor.findChar(cell) == null){ + GLog.i("Okay, I'll hold that position"); + ghost.aggro(null); + ghost.state = ghost.WANDERING; + ghost.defendingPos = cell; + ghost.movingToDefendPos = true; + return; + } + + if (ghost.fieldOfView == null || ghost.fieldOfView.length != Dungeon.level.length()){ + ghost.fieldOfView = new boolean[Dungeon.level.length()]; + } + Dungeon.level.updateFieldOfView( ghost, ghost.fieldOfView ); + + if (Actor.findChar(cell) == Dungeon.hero){ + GLog.i("Okay, I'll follow you."); + ghost.aggro(null); + ghost.state = ghost.WANDERING; + ghost.defendingPos = -1; + + } else if (Actor.findChar(cell).alignment == Char.Alignment.ENEMY){ + GLog.i("Okay, I'll attack that enemy."); + ghost.aggro(Actor.findChar(cell)); + ghost.setTarget(cell); + + } + } + + @Override + public String prompt() { + return "\"What should I do?\""; + } + }; public static class Petal extends Item { @@ -372,6 +432,9 @@ public class DriedRose extends Artifact { properties.add(Property.UNDEAD); } + private int defendingPos = -1; + private boolean movingToDefendPos = false; + private DriedRose rose = null; public GhostHero(){ @@ -451,13 +514,16 @@ public class DriedRose extends Artifact { protected Char chooseEnemy() { Char enemy = super.chooseEnemy(); - //will never attack something far from the player - if (enemy != null && Dungeon.level.mobs.contains(enemy) - && Dungeon.level.distance(enemy.pos, Dungeon.hero.pos) <= 8){ + int targetPos = defendingPos != -1 ? defendingPos : Dungeon.hero.pos; + + //will never attack something far from their target + if (enemy != null + && Dungeon.level.mobs.contains(enemy) + && (Dungeon.level.distance(enemy.pos, targetPos) <= 8)){ return enemy; - } else { - return null; } + + return null; } @Override @@ -572,7 +638,11 @@ public class DriedRose extends Artifact { } return block; } - + + private void setTarget(int cell) { + target = cell; + } + @Override public boolean interact() { updateRose(); @@ -609,10 +679,28 @@ public class DriedRose extends Artifact { if (rose != null) { rose.ghost = null; rose.ghostID = -1; + rose.defaultAction = AC_SUMMON; } super.destroy(); } + private static final String DEFEND_POS = "defend_pos"; + private static final String MOVING_TO_DEFEND = "moving_to_defend"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(DEFEND_POS, defendingPos); + bundle.put(MOVING_TO_DEFEND, movingToDefendPos); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + if (bundle.contains(DEFEND_POS)) defendingPos = bundle.getInt(DEFEND_POS); + movingToDefendPos = bundle.getBoolean(MOVING_TO_DEFEND); + } + { immunities.add( ToxicGas.class ); immunities.add( CorrosiveGas.class ); @@ -626,7 +714,7 @@ public class DriedRose extends Artifact { @Override public boolean act( boolean enemyInFOV, boolean justAlerted ) { - if ( enemyInFOV ) { + if ( enemyInFOV && !movingToDefendPos ) { enemySeen = true; @@ -640,13 +728,15 @@ public class DriedRose extends Artifact { enemySeen = false; int oldPos = pos; + target = defendingPos != -1 ? defendingPos : Dungeon.hero.pos; //always move towards the hero when wandering - if (getCloser( target = Dungeon.hero.pos )) { - //moves 2 tiles at a time when returning to the hero from a distance - if (!Dungeon.level.adjacent(Dungeon.hero.pos, pos)){ - getCloser( target = Dungeon.hero.pos ); + if (getCloser( target )) { + //moves 2 tiles at a time when returning to the hero + if (defendingPos == -1 && !Dungeon.level.adjacent(target, pos)){ + getCloser( target ); } spend( 1 / speed() ); + if (pos == defendingPos) movingToDefendPos = false; return moveSprite( oldPos, pos ); } else { spend( TICK ); @@ -661,6 +751,7 @@ public class DriedRose extends Artifact { //************************************************************************************ //This is a bunch strings & string arrays, used in all of the sad ghost's voice lines. //************************************************************************************ + //FIXME, need to go over these for final polish (inc. translations!) private static final String VOICE_INTRODUCE = "My spirit is bound to this rose, it was very precious to me, a "+ "gift from my love whom I left on the surface.\n\nI cannot return to him, but thanks to you I have a " + diff --git a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/items/items.properties b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/items/items.properties index 9a947547d..fc34afffe 100644 --- a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/items/items.properties +++ b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/items/items.properties @@ -187,6 +187,7 @@ items.artifacts.cloakofshadows$cloakstealth.desc=Your cloak of shadows is granti items.artifacts.driedrose.name=dried rose items.artifacts.driedrose.ac_summon=SUMMON +items.artifacts.driedrose.ac_direct=DIRECT items.artifacts.driedrose.ac_outfit=OUTFIT items.artifacts.driedrose.spawned=You have already summoned the ghost. items.artifacts.driedrose.no_charge=Your rose isn't fully charged yet. @@ -206,8 +207,8 @@ items.artifacts.driedrose$petal.desc=A frail dried up petal, which has somehow s items.artifacts.driedrose$ghosthero.name=sad ghost items.artifacts.driedrose$ghosthero.def_verb=evaded items.artifacts.driedrose$ghosthero.hello=Hello again %s. -items.artifacts.driedrose$ghosthero.introduce=My spirit is bound to this rose, it was very precious to me, a gift from my love whom I left on the surface.\n\nI cannot return to him, but thanks to you I have a second chance to complete my journey. When I am able I will respond to your call and fight with you.\n\nHopefully you may succeed where I failed... -items.artifacts.driedrose$ghosthero.desc=A frail looking ethereal figure with a humanoid shape. Its power seems tied to the rose you have.\n\nThis ghost may not be much, but it seems to be your only true friend down here. +items.artifacts.driedrose$ghosthero.introduce=My spirit is bound to this rose, it was very precious to me, a gift from my love whom I left on the surface.\n\nI cannot return to him, but thanks to you I have a second chance to complete my journey. When I am able I will respond to your call and fight with you.\n\nAs long as you have the rose I can hear your voice, so you can use it to direct me from afar.\n\nHopefully you may succeed where I failed... +items.artifacts.driedrose$ghosthero.desc=A frail looking ethereal figure with a humanoid shape. Their power seems tied to the rose you have.\n\nThis ghost may not be much, they seem to be your only true friend down here. items.artifacts.driedrose$wndghosthero.title=Ghost's Equipment items.artifacts.driedrose$wndghosthero.desc=The ghost is weak on their own, but their form is solid enough to be equipped with a weapon and armor. They will be able to use these items just like you do.\n\nThe ghost can currently equip items requiring up to _%d strength._ items.artifacts.driedrose$wndghosthero.weapon_prompt=Select a weapon