v0.7.0: rebalanced wand of transmutation, added a barrier buff

This commit is contained in:
Evan Debenham 2018-06-30 20:42:26 -04:00
parent 4de7f3752e
commit ec6e88139c
8 changed files with 223 additions and 151 deletions

View File

@ -0,0 +1,84 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2018 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.buffs;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.watabou.utils.Bundle;
//FIXME managing shield buffs is starting to get cumbersome, should come up with a way to have them interact easily
public class Barrier extends Buff {
private int level = 1;
//TODO icon and description for phase 2
@Override
public boolean act() {
if (target.SHLD == level){
target.SHLD -= 1;
}
level -= 1;
if (target.SHLD <= level) {
target.SHLD = level;
}
if (level <= 0){
detach();
}
spend( TICK );
return true;
}
public void set( int shielding ){
if (level < shielding){
level = shielding;
if (target.SHLD < level){
target.SHLD = level;
}
}
}
@Override
public void fx(boolean on) {
if (on) target.sprite.add(CharSprite.State.SHIELDED);
else target.sprite.remove(CharSprite.State.SHIELDED);
}
private static final String LEVEL = "level";
@Override
public void storeInBundle( Bundle bundle ) {
super.storeInBundle( bundle );
bundle.put( LEVEL, level);
}
@Override
public void restoreFromBundle( Bundle bundle ) {
super.restoreFromBundle( bundle );
level = bundle.getInt( LEVEL );
}
}

View File

@ -0,0 +1,79 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2018 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.effects;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.watabou.glwrap.Blending;
import com.watabou.noosa.Game;
import com.watabou.utils.PointF;
public class ShieldHalo extends Halo {
private CharSprite target;
private float phase;
public ShieldHalo( CharSprite sprite ) {
//rectangular sprite to circular radius. Pythagorean theorem
super( (float)Math.sqrt(Math.pow(sprite.width()/2f, 2) + Math.pow(sprite.height()/2f, 2)), 0xBBAACC, 1f );
am = -0.33f;
aa = +0.33f;
target = sprite;
phase = 1;
}
@Override
public void update() {
super.update();
if (phase < 1) {
if ((phase -= Game.elapsed) <= 0) {
killAndErase();
} else {
scale.set( (2 - phase) * radius / RADIUS );
am = phase * (-1);
aa = phase * (+1);
}
}
if (visible = target.visible) {
PointF p = target.center();
point( p.x, p.y );
}
}
@Override
public void draw() {
Blending.setLightMode();
super.draw();
Blending.setNormalMode();
}
public void putOut() {
phase = 0.999f;
}
}

View File

@ -25,6 +25,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
@ -32,18 +33,10 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.BloodParticle;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
@ -73,30 +66,36 @@ public class WandOfTransfusion extends Wand {
int cell = beam.collisionPos;
Char ch = Actor.findChar(cell);
Heap heap = Dungeon.level.heaps.get(cell);
//this wand does a bunch of different things depending on what it targets.
//if we find a character..
if (ch != null && ch instanceof Mob){
// 10% of max hp
int selfDmg = (int)Math.ceil(curUser.HT*0.10f);
processSoulMark(ch, chargesPerCast());
//heals an ally, or a charmed enemy
//this wand does different things depending on the target.
//heals/shields an ally, or a charmed enemy
if (ch.alignment == Char.Alignment.ALLY || ch.buff(Charm.class) != null){
int missingHP = ch.HT - ch.HP;
//heals 30%+3%*lvl missing HP.
int healing = (int)Math.ceil((missingHP * (0.30f+(0.03f*level()))));
int healing = selfDmg + 3*level();
int shielding = -(ch.HT - ch.HP - healing);
if (shielding > 0){
healing -= shielding;
}
ch.HP += healing;
ch.sprite.emitter().burst(Speck.factory(Speck.HEALING), 1 + level() / 2);
ch.sprite.showStatus(CharSprite.POSITIVE, "+%dHP", healing);
Buff.affect(ch, Barrier.class).set(shielding);
ch.sprite.emitter().burst(Speck.factory(Speck.HEALING), 2 + level() / 2);
ch.sprite.showStatus(CharSprite.POSITIVE, "+%dHP", healing + shielding);
//harms the undead
} else if (ch.properties().contains(Char.Property.UNDEAD)){
//deals 30%+5%*lvl total HP.
int damage = (int) Math.ceil(ch.HT*(0.3f+(0.05f*level())));
int damage = selfDmg + 3*level();
ch.damage(damage, this);
ch.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10 + level());
Sample.INSTANCE.play(Assets.SND_BURNING);
@ -104,74 +103,27 @@ public class WandOfTransfusion extends Wand {
//charms an enemy
} else {
float duration = 5+level();
Buff.affect(ch, Charm.class, duration).object = curUser.id();
duration *= Random.Float(0.75f, 1f);
Buff.affect(curUser, Charm.class, duration).object = ch.id();
Buff.affect(ch , Charm.class, 5 + level() ).object = curUser.id();
Buff.affect(curUser , Charm.class, 5 ).object = ch.id();
ch.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 );
curUser.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 );
}
//if we find an item...
} else if (heap != null && heap.type == Heap.Type.HEAP){
Item item = heap.peek();
//30% + 10%*lvl chance to uncurse the item and reset it to base level if degraded.
if (item != null && Random.Float() <= 0.3f+level()*0.1f){
if (item.cursed){
item.cursed = false;
CellEmitter.get(cell).start( ShadowParticle.UP, 0.05f, 10 );
Sample.INSTANCE.play(Assets.SND_BURNING);
}
int lvldiffFromBase = item.level() - (item instanceof Ring ? 1 : 0);
if (lvldiffFromBase < 0){
item.upgrade(-lvldiffFromBase);
CellEmitter.get(cell).start(Speck.factory(Speck.UP), 0.2f, 3);
Sample.INSTANCE.play(Assets.SND_BURNING);
}
}
//if we find some trampled grass...
} else if (Dungeon.level.map[cell] == Terrain.GRASS) {
//regrow one grass tile, suuuuuper useful...
Dungeon.level.set(cell, Terrain.HIGH_GRASS);
GameScene.updateMap(cell);
CellEmitter.get( cell ).burst(LeafParticle.LEVEL_SPECIFIC, 4);
//If we find embers...
} else if (Dungeon.level.map[cell] == Terrain.EMBERS) {
//30% + 3%*lvl chance to grow a random plant, or just regrow grass.
if (Random.Float() <= 0.3f+level()*0.03f) {
Dungeon.level.plant((Plant.Seed) Generator.random(Generator.Category.SEED), cell);
CellEmitter.get( cell ).burst(LeafParticle.LEVEL_SPECIFIC, 8);
GameScene.updateMap(cell);
} else{
Dungeon.level.set(cell, Terrain.HIGH_GRASS);
GameScene.updateMap(cell);
CellEmitter.get( cell ).burst(LeafParticle.LEVEL_SPECIFIC, 4);
}
} else
return; //don't damage the hero if we can't find a target;
if (!freeCharge) {
damageHero();
damageHero(selfDmg);
} else {
freeCharge = false;
}
}
}
//this wand costs health too
private void damageHero(){
// 10% of max hp
int damage = (int)Math.ceil(curUser.HT*0.10f);
private void damageHero(int damage){
curUser.damage(damage, this);
if (!curUser.isAlive()){

View File

@ -28,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.DarkBlock;
import com.shatteredpixel.shatteredpixeldungeon.effects.EmoIcon;
import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText;
import com.shatteredpixel.shatteredpixeldungeon.effects.IceBlock;
import com.shatteredpixel.shatteredpixeldungeon.effects.ShieldHalo;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.effects.TorchHalo;
@ -78,7 +79,7 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
protected float shadowOffset = 0.25f;
public enum State {
BURNING, LEVITATING, INVISIBLE, PARALYSED, FROZEN, ILLUMINATED, CHILLED, DARKENED, MARKED, HEALING
BURNING, LEVITATING, INVISIBLE, PARALYSED, FROZEN, ILLUMINATED, CHILLED, DARKENED, MARKED, HEALING, SHIELDED
}
protected Animation idle;
@ -100,7 +101,8 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
protected IceBlock iceBlock;
protected DarkBlock darkBlock;
protected TorchHalo halo;
protected TorchHalo light;
protected ShieldHalo shield;
protected AlphaTweener invisible;
protected EmoIcon emo;
@ -331,7 +333,7 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
paused = true;
break;
case ILLUMINATED:
GameScene.effect( halo = new TorchHalo( this ) );
GameScene.effect( light = new TorchHalo( this ) );
break;
case CHILLED:
chilled = emitter();
@ -347,6 +349,10 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
case HEALING:
healing = emitter();
healing.pour(Speck.factory(Speck.HEALING), 0.5f);
break;
case SHIELDED:
GameScene.effect( shield = new ShieldHalo( this ));
break;
}
}
@ -382,8 +388,8 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
paused = false;
break;
case ILLUMINATED:
if (halo != null) {
halo.putOut();
if (light != null) {
light.putOut();
}
break;
case CHILLED:
@ -410,6 +416,11 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
healing = null;
}
break;
case SHIELDED:
if (shield != null){
shield.putOut();
}
break;
}
}

View File

@ -49,11 +49,11 @@ public class HeroSprite extends CharSprite {
public HeroSprite() {
super();
link( Dungeon.hero );
texture( Dungeon.hero.heroClass.spritesheet() );
updateArmor();
link( Dungeon.hero );
if (ch.isAlive())
idle();
else
@ -62,7 +62,7 @@ public class HeroSprite extends CharSprite {
public void updateArmor() {
TextureFilm film = new TextureFilm( tiers(), ((Hero)ch).tier(), FRAME_WIDTH, FRAME_HEIGHT );
TextureFilm film = new TextureFilm( tiers(), Dungeon.hero.tier(), FRAME_WIDTH, FRAME_HEIGHT );
idle = new Animation( 1, true );
idle.frames( film, 0, 0, 0, 1, 0, 0, 1, 1 );
@ -87,7 +87,7 @@ public class HeroSprite extends CharSprite {
read = new Animation( 20, false );
read.frames( film, 19, 20, 20, 20, 20, 20, 20, 20, 20, 19 );
if (ch.isAlive())
if (Dungeon.hero.isAlive())
idle();
else
die();

View File

@ -23,17 +23,14 @@ package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.effects.Halo;
import com.shatteredpixel.shatteredpixeldungeon.effects.ShieldHalo;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle;
import com.watabou.glwrap.Blending;
import com.watabou.noosa.Game;
import com.watabou.noosa.TextureFilm;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.PointF;
public class WandmakerSprite extends MobSprite {
private Shield shield;
private ShieldHalo shield;
public WandmakerSprite() {
super();
@ -57,19 +54,14 @@ public class WandmakerSprite extends MobSprite {
@Override
public void link( Char ch ) {
super.link( ch );
if (shield == null) {
parent.add( shield = new Shield() );
}
add(State.SHIELDED);
}
@Override
public void die() {
super.die();
if (shield != null) {
shield.putOut();
}
remove(State.SHIELDED);
emitter().start( ElmoParticle.FACTORY, 0.03f, 60 );
if (visible) {
@ -77,50 +69,4 @@ public class WandmakerSprite extends MobSprite {
}
}
public class Shield extends Halo {
private float phase;
public Shield() {
super( 9, 0xBBAACC, 1f );
am = -0.33f;
aa = +0.33f;
phase = 1;
}
@Override
public void update() {
super.update();
if (phase < 1) {
if ((phase -= Game.elapsed) <= 0) {
killAndErase();
} else {
scale.set( (2 - phase) * radius / RADIUS );
am = phase * (-1);
aa = phase * (+1);
}
}
if (visible = WandmakerSprite.this.visible) {
PointF p = WandmakerSprite.this.center();
point(p.x, p.y );
}
}
@Override
public void draw() {
Blending.setLightMode();
super.draw();
Blending.setNormalMode();
}
public void putOut() {
phase = 0.999f;
}
}
}

View File

@ -52,7 +52,7 @@ public class CharHealthIndicator extends HealthBar {
x = sprite.x + sprite.width()/6f;
y = sprite.y - 2;
level( target );
visible = target.HP < target.HT;
visible = target.HP < target.HT || target.SHLD > 0;
} else {
visible = false;
}

View File

@ -810,7 +810,7 @@ items.wands.wandoftransfusion.staff_name=staff of transfusion
items.wands.wandoftransfusion.ondeath=You killed yourself with your own Wand of Transfusion...
items.wands.wandoftransfusion.charged=Your staff is charged with the life energy of your enemy!
items.wands.wandoftransfusion.desc=A fairly plainly shaped wand, it stands out due to its magenta hue and pitch black gem at the tip.
items.wands.wandoftransfusion.stats_desc=This wand will take some of your life energy and blast it at a target. This effect is very versatile: allies will be healed, enemies will be temporarily charmed, and hostile undead will take considerable damage. The life drain is significant though, using this wand will deal damage to you in addition to consuming charges.
items.wands.wandoftransfusion.stats_desc=This wand will sap some of your life energy and blast it at a target. This effect is very versatile: allies will be healed or shielded, enemies will be temporarily charmed, and hostile undead will take considerable damage.
items.wands.wandofcorrosion.name=wand of corrosion
items.wands.wandofcorrosion.staff_name=staff of corrosion