Add Beta21-p1.8

All Commit
This commit is contained in:
LingASDJ 2022-09-20 00:04:45 +08:00
parent 4709a3dbed
commit 9654542d1f
34 changed files with 2359 additions and 46 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,3 +1,11 @@
###SPD1.3
text.herostat.item_wnd_depth = 层数:
text.herostat.item_wnd_mimic = 宝箱怪:
text.herostat.item_wnd_reward = 任务:
text.herostat.item_wnd_cursed = 诅咒的
text.herostat.item_basic = 常规:
text.herostat.item_enter = 查看物品生成
###MLPD ###MLPD
actors.mobs.bosses.crossdiedtower.dead=控制目标已丢失,已自动摧毁…… actors.mobs.bosses.crossdiedtower.dead=控制目标已丢失,已自动摧毁……
actors.mobs.bosses.crossdiedtower.name=激光十字晶柱 actors.mobs.bosses.crossdiedtower.name=激光十字晶柱

View File

@ -6,10 +6,6 @@ items.bombs.laserpython.name=激光十字晶柱召唤器
items.bombs.laserpython.desc=这枚召唤器会在爆炸后立刻生成一个十字晶柱。 items.bombs.laserpython.desc=这枚召唤器会在爆炸后立刻生成一个十字晶柱。
items.bombs.laserpython.didnot_pick=你无法拾取该物品…… items.bombs.laserpython.didnot_pick=你无法拾取该物品……
items.weapon.melee.endingblade.ac_lastcrystal=Γ激光晶柱Γ
items.weapon.melee.endingblade.ac_diedghost=✦死亡宣告✦
items.weapon.melee.endingblade.ac_healreset=_亡者归来_
items.artifacts.wraithamulet.name=暗金宝石护符 items.artifacts.wraithamulet.name=暗金宝石护符
items.artifacts.wraithamulet.desc=来自于异世界的产物,能使自己进入虚无化。当护符能量足够多的时候,你还可以对敌人实行一次暗夜袭击,护符能量将会随着时间自动恢复。\n\n这个护符是证明给强者之人用的冰雪魔女已经输的心服口服现在她将此护身符给予给你。 items.artifacts.wraithamulet.desc=来自于异世界的产物,能使自己进入虚无化。当护符能量足够多的时候,你还可以对敌人实行一次暗夜袭击,护符能量将会随着时间自动恢复。\n\n这个护符是证明给强者之人用的冰雪魔女已经输的心服口服现在她将此护身符给予给你。
items.artifacts.wraithamulet.ac_ghost=遁入虚无 items.artifacts.wraithamulet.ac_ghost=遁入虚无
@ -95,9 +91,14 @@ items.quest.skeletongold.wow=染血金币发出诡异的光芒,你感到下层
items.weapon.melee.endingblade.name=终焉 items.weapon.melee.endingblade.name=终焉
items.weapon.melee.endingblade.desc=不知道从哪来的一个拆开的场记板貌似沾染了焰之诅咒会随机产生_一种诅咒_有着强大的侵蚀能力一旦装备了它就无法脱身了……\n\n这个武器在攻击敌人的时候能吸收一定的浊焰能量,在汲取一定的浊焰能量后会产生新的能力以及自我升级。\n\n当前的浊焰能量: items.weapon.melee.endingblade.desc=不知道从哪来的一个拆开的场记板貌似沾染了焰之诅咒会随机产生_一种诅咒_有着强大的侵蚀能力一旦装备了它就无法脱身了……\n\n这个武器在攻击敌人的时候能吸收一定的浊焰能量,在汲取一定的浊焰能量后会产生新的能力以及自我升级。\n\n当前的浊焰能量:
items.weapon.melee.endingblade.desc_2=\n\n死亡宣告当前冷却值为:
items.weapon.melee.endingblade.cursed=当你装备上这个武器后,一股无形的力量将你束缚住…… items.weapon.melee.endingblade.cursed=当你装备上这个武器后,一股无形的力量将你束缚住……
items.weapon.melee.endingblade.donot_eqip=终焉的诅咒已经浸染你的身体,你无力脱下它。 items.weapon.melee.endingblade.donot_eqip=终焉的诅咒已经浸染你的身体,你无力脱下它。
items.weapon.melee.endingblade.ac_lastcrystal=Γ激光晶柱Γ
items.weapon.melee.endingblade.ac_diedghost=✦死亡宣告✦
items.weapon.melee.endingblade.ac_healreset=_亡者归来_
#MLPDSTOREYBOOKS #MLPDSTOREYBOOKS
items.books.bookslist.hellfirebooks.name=《浊焰事件》 items.books.bookslist.hellfirebooks.name=《浊焰事件》

View File

@ -67,6 +67,10 @@ badges$badge.games_played_3=进行250场游戏
badges$badge.games_played_4=进行1000场游戏 badges$badge.games_played_4=进行1000场游戏
badges$badge.godd_make=老人与海\n累计完成老杖匠的全部任务\n\n_奖励0层随机戒指(四大基座上)_ badges$badge.godd_make=老人与海\n累计完成老杖匠的全部任务\n\n_奖励0层随机戒指(四大基座上)_
badges$badge.clear_water=净化大师\n完成挑战:污泥浊水\n\n_奖励敬请期待_ badges$badge.clear_water=净化大师\n完成挑战:污泥浊水\n\n_奖励敬请期待_
badges$badge.ghostdage=幽灵大哥\n_在幽灵处获得一次+4品质武器或护甲\n\n_(镀层需求:+5品质武器)
//badges$badge.ghostdage=幽灵大哥\n在幽灵处获得一次_+5_品质武器或护甲\n\nΞ你已成功镀层Ξ
badges$badge.halofire_died=死于磷火烈焰 badges$badge.halofire_died=死于磷火烈焰
badges$badge.happy_end=幸福结局 badges$badge.happy_end=幸福结局
badges$badge.champion_1x=开启1项挑战通关 badges$badge.champion_1x=开启1项挑战通关

View File

@ -2,6 +2,9 @@ windows.textchallenges.seed_custom_title = 种子
windows.textchallenges.hint = 不输入即为随机种子 windows.textchallenges.hint = 不输入即为随机种子
windows.textchallenges.delete_seed_input = 清除 windows.textchallenges.delete_seed_input = 清除
windows.wndsettings$helptab.title=辅助功能
windows.wndsettings$helptab.helpsettings=启用物品生成查询器
windows.wndkingshop.buy=购买 windows.wndkingshop.buy=购买
windows.wndkingshop.cancel=取消 windows.wndkingshop.cancel=取消
windows.wndkingshop.king=商人领主 windows.wndkingshop.king=商人领主

View File

@ -109,6 +109,7 @@ public class Badges {
GAMES_PLAYED_1 ( 54, true ), GAMES_PLAYED_1 ( 54, true ),
GODD_MAKE ( 82 ), GODD_MAKE ( 82 ),
CLEAR_WATER ( 83 ), CLEAR_WATER ( 83 ),
GHOSTDAGE ( 84 ),
//gold //gold
PIRANHAS ( 64 ), PIRANHAS ( 64 ),
//these names are a bit outdated, but it doesn't really matter. //these names are a bit outdated, but it doesn't really matter.

View File

@ -686,6 +686,9 @@ public class Dungeon {
hero.curAction = hero.lastAction = null; hero.curAction = hero.lastAction = null;
//SPD
LevelSwitchListener.onLevelSwitch();
observe(); observe();
try { try {
saveAll(); saveAll();

View File

@ -0,0 +1,12 @@
package com.shatteredpixel.shatteredpixeldungeon;
import com.shatteredpixel.shatteredpixeldungeon.custom.ch.GameTracker;
public class LevelSwitchListener {
public static void onLevelSwitch(){
GameTracker gmt = Dungeon.hero.buff(GameTracker.class);
if(gmt != null){
gmt.onNewLevel();
}
}
}

View File

@ -109,9 +109,7 @@ public class SPDSettings extends GameSettings {
private static final String KEY_PAGE = "page_ui"; private static final String KEY_PAGE = "page_ui";
private static final String KEY_PCUI = "pc_ui"; private static final String HelpSettings = "helpsettings";
private static final String KEY_SWAP = "quickswap";
public static void fullscreen( boolean value ) { public static void fullscreen( boolean value ) {
put( KEY_FULLSCREEN, value ); put( KEY_FULLSCREEN, value );
@ -447,6 +445,14 @@ public class SPDSettings extends GameSettings {
return getBoolean(KEY_DARK, false); return getBoolean(KEY_DARK, false);
} }
public static void HelpSettings(boolean value) {
put( HelpSettings, value );
}
public static boolean HelpSettings() {
return getBoolean(HelpSettings, false);
}
public static boolean ClassSkin() { public static boolean ClassSkin() {
return getBoolean(KEY_SKIN, false); return getBoolean(KEY_SKIN, false);
} }

View File

@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon; package com.shatteredpixel.shatteredpixeldungeon;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.SparseArray;
public class Statistics { public class Statistics {
public static int realdeepestFloor; public static int realdeepestFloor;
@ -54,6 +55,15 @@ public class Statistics {
public static boolean deadshoppingdied = false; public static boolean deadshoppingdied = false;
//Directly add float time will cause accuracy lose and stop timing if time is long enough
//so use long to record seconds, float to count sub-seconds.
//SPD-V1.3.2-ITEM SPAWN CODE
public static long real_seconds = 0;
public static float second_elapsed = 0;
public static float turnsPassed = 0f;
public static SparseArray<Boolean> floorsExplored = new SparseArray<>();
public static void reset() { public static void reset() {
goldCollected = 0; goldCollected = 0;
@ -80,6 +90,10 @@ public class Statistics {
deadshoppingdied = false; deadshoppingdied = false;
second_elapsed = 0f;
real_seconds = 0;
turnsPassed = 0f;
} }
private static final String GOLD = "score"; private static final String GOLD = "score";
@ -133,6 +147,11 @@ public class Statistics {
bundle.put( NOSHOPPING, fireGirlnoshopping ); bundle.put( NOSHOPPING, fireGirlnoshopping );
bundle.put( SHOPPINGDIED, deadshoppingdied ); bundle.put( SHOPPINGDIED, deadshoppingdied );
//SPD
bundle.put("real_time_passed", second_elapsed);
bundle.put("real_seconds_passed", real_seconds);
bundle.put("turns_passed", turnsPassed);
} }
public static void restoreFromBundle( Bundle bundle ) { public static void restoreFromBundle( Bundle bundle ) {
@ -158,6 +177,11 @@ public class Statistics {
fireGirlnoshopping = bundle.getBoolean( NOSHOPPING ); fireGirlnoshopping = bundle.getBoolean( NOSHOPPING );
deadshoppingdied = bundle.getBoolean( SHOPPINGDIED ); deadshoppingdied = bundle.getBoolean( SHOPPINGDIED );
//SPD
second_elapsed = bundle.getFloat("real_time_passed");
real_seconds = bundle.getLong("real_seconds_passed");
turnsPassed = bundle.getFloat("turns_passed");
} }
public static void preview( GamesInProgress.Info info, Bundle bundle ){ public static void preview( GamesInProgress.Info info, Bundle bundle ){

View File

@ -79,6 +79,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Monk; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Monk;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Snake; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Snake;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.lb.BlackSoul; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.lb.BlackSoul;
import com.shatteredpixel.shatteredpixeldungeon.custom.ch.GameTracker;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.CheckedCell; import com.shatteredpixel.shatteredpixeldungeon.effects.CheckedCell;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
@ -494,6 +495,7 @@ public class Hero extends Char {
} }
Buff.affect( this, Regeneration.class ); Buff.affect( this, Regeneration.class );
Buff.affect( this, Hunger.class ); Buff.affect( this, Hunger.class );
Buff.affect(this, GameTracker.class);
} }
public int tier() { public int tier() {

View File

@ -313,10 +313,10 @@ public class Ghost extends NPC {
Generator.Category c = Generator.wepTiers[wepTier - 1]; Generator.Category c = Generator.wepTiers[wepTier - 1];
weapon = (MeleeWeapon) Reflection.newInstance(c.classes[Random.chances(c.probs)]); weapon = (MeleeWeapon) Reflection.newInstance(c.classes[Random.chances(c.probs)]);
//30%:+0, 25%:+1, 15%:+2, 10%:+3, 15%:+4, 5%+5 //26%:+0, 25%:+1, 15%:+2, 10%:+3, 5%:+4, 5%+5
float itemLevelRoll = Random.Float(); float itemLevelRoll = Random.Float();
int itemLevel; int itemLevel;
if (itemLevelRoll < 0.1f){ if (itemLevelRoll < 0.74f){
itemLevel = 0; itemLevel = 0;
} else if (itemLevelRoll < 0.75f){ } else if (itemLevelRoll < 0.75f){
itemLevel = 1; itemLevel = 1;

View File

@ -0,0 +1,87 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.custom.messages.M;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.noosa.Image;
import com.watabou.utils.Bundle;
public class AbsoluteBlindness extends Buff {
{
actPriority = VFX_PRIO;
announced = true;
type=buffType.NEGATIVE;
}
protected float left=0f;
private int storedViewDistance;
@Override
public boolean act(){
spend(TICK);
if(target.viewDistance>0 && storedViewDistance != target.viewDistance){
storedViewDistance = target.viewDistance;
}
target.viewDistance = 0;
left-=1f;
if(left<0) detach();
return true;
}
@Override
public void storeInBundle(Bundle b){
super.storeInBundle(b);
b.put("stroedVD", storedViewDistance);
b.put("blindLeft", left);
}
@Override
public void restoreFromBundle(Bundle b){
super.restoreFromBundle(b);
storedViewDistance = b.getInt("stroedVD");
left = b.getFloat("blindLeft") + 1f;
}
//deprecate
public AbsoluteBlindness storeVD(int vd){
if(vd>0) {
storedViewDistance = vd;
}
return this;
}
public AbsoluteBlindness addLeft(float left){
this.left += left;
return this;
}
@Override
public void detach(){
//target.viewDistance = storedViewDistance;
target.viewDistance = Dungeon.level.viewDistance;
Dungeon.observe();
super.detach();
}
@Override
public int icon(){
return BuffIndicator.BLINDNESS;
}
@Override
public void tintIcon(Image icon){
icon.hardlight(0x3366D4);
}
@Override
public String toString() {
return M.L(this, "name");
}
@Override
public String heroMessage() {
return M.L(this, "heromsg");
}
@Override
public String desc() {
return M.L(this, "desc");
}
}

View File

@ -0,0 +1,215 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.custom.utils.GME;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.utils.Bundle;
public class AttributeModifier extends FlavourBuff {
{
type = buffType.NEUTRAL;
announced = true;
}
public float atk_m = 1f;
public float def_m = 1f;
public float acc_m = 1f;
public float eva_m = 1f;
public float dmg_m = 1f;
public float hp_m = 1f;
public float atk_l = 0f;
public float def_l = 0f;
public float acc_l = 0f;
public float eva_l = 0f;
public float dmg_l = 0f;
public float hp_l = 0f;
public int orig_HT = -1;
public void merge(AttributeModifier another){
atk_m *= another.atk_m;
def_m *= another.def_m;
acc_m *= another.acc_m;
eva_m *= another.eva_m;
dmg_m *= another.dmg_m;
hp_m *= another.hp_m;
atk_l += another.atk_l;
def_l += another.def_l;
acc_l += another.acc_l;
eva_l += another.eva_l;
dmg_l += another.dmg_l;
hp_l += another.hp_l;
}
public int affectAtk(float attackPower){
return GME.accurateRound(attackPower * atk_m + atk_l);
}
public int affectDef(float defensePower){
return GME.accurateRound(defensePower * def_m + def_l);
}
public float affectAcc(float accuracy){
return accuracy * acc_m + acc_l;
}
public float affectEva(float evasion){
return evasion * eva_m + eva_l;
}
public int affectDmg(float damage){
return GME.accurateRound(damage * dmg_m + dmg_l);
}
public void affectHp(Char ch){
float percent = (float) ch.HP / ch.HT;
//HT of Char is bundled, need to roll back first
if(orig_HT < 0) {
orig_HT = ch.HT;
}else{
ch.HT = orig_HT;
}
ch.HT = (int)(ch.HT * hp_m + hp_l);
if(ch.HT <= 0){
ch.HT = 1;
}
ch.HP = GME.accurateRound(ch.HT * percent);
if(ch.HP <= 0){
ch.HP = 1;
}
}
public void setHPBack(Char ch){
float percent = (float) ch.HP / ch.HT;
ch.HT = orig_HT;
boolean alive = ch.isAlive();
ch.HP = GME.accurateRound(ch.HT * percent);
if(ch.HP <= 0 && alive) ch.HP = 1;
}
public AttributeModifier setAtk(float m, float l){
atk_m = m; atk_l = l;
return this;
}
public AttributeModifier setDef(float m, float l){
def_m = m; def_l = l;
return this;
}
public AttributeModifier setAcc(float m, float l){
acc_m = m; acc_l = l;
return this;
}
public AttributeModifier setEva(float m, float l){
eva_m = m; eva_l = l;
return this;
}
public AttributeModifier setDmg(float m, float l){
dmg_m = m; dmg_l = l;
return this;
}
public AttributeModifier setHP(float m, float l){
hp_m = m; hp_l = l;
if(target != null){
affectHp(target);
}
return this;
}
public AttributeModifier setAll(float[] mul, float[] add){
atk_m = mul[0];
def_m = mul[1];
acc_m = mul[2];
eva_m = mul[3];
dmg_m = mul[4];
hp_m = mul[5];
atk_l = add[0];
def_l = add[1];
acc_l = add[2];
eva_l = add[3];
dmg_l = add[4];
hp_l = add[5];
if(target != null){
affectHp(target);
}
return this;
}
@Override
public boolean attachTo(Char target) {
for(Buff b: target.buffs()){
if(b instanceof AttributeModifier){
merge((AttributeModifier) b);
b.detach();
}
}
boolean ret_val = super.attachTo(target);
if(ret_val){
affectHp(target);
}
return ret_val;
}
@Override
public void detach() {
setHPBack(target);
super.detach();
}
@Override
public int icon() {
return BuffIndicator.COMBO;
}
@Override
public String toString() {
return Messages.get(this, "name");
}
@Override
public String desc() {
return Messages.get(this, "desc", atk_m, atk_l, def_m, def_l, acc_m, acc_l, eva_m, eva_l, dmg_m, dmg_l, hp_m, hp_l, target.HP, target.HT);
}
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put("atk_mul", atk_m);
bundle.put("def_mul", def_m);
bundle.put("acc_mul", acc_m);
bundle.put("eva_mul", eva_m);
bundle.put("dmg_mul", dmg_m);
bundle.put("hp_mul", hp_m);
bundle.put("atk_lin", atk_l);
bundle.put("def_lin", def_l);
bundle.put("acc_lin", acc_l);
bundle.put("eva_lin", eva_l);
bundle.put("dmg_lin", dmg_l);
bundle.put("hp_lin", hp_l);
bundle.put("orig_hp", orig_HT);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
atk_m = bundle.getFloat("atk_mul");
def_m = bundle.getFloat("def_mul");
acc_m = bundle.getFloat("acc_mul");
eva_m = bundle.getFloat("eva_mul");
dmg_m = bundle.getFloat("dmg_mul");
hp_m = bundle.getFloat("hp_mul");
atk_l = bundle.getFloat("atk_lin");
def_l = bundle.getFloat("def_lin");
acc_l = bundle.getFloat("acc_lin");
eva_l = bundle.getFloat("eva_lin");
dmg_l = bundle.getFloat("dmg_lin");
hp_l = bundle.getFloat("hp_lin");
orig_HT = bundle.getInt("orig_hp");
}
}

View File

@ -0,0 +1,151 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.custom.messages.M;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.effects.Wound;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Bundle;
import com.watabou.utils.PointF;
import com.watabou.utils.Random;
public class ConsistBleeding extends Buff{
protected float[] lasting;
protected float[] dmg;
protected float percentDamage=0;
public ConsistBleeding(){
lasting = new float[5];
dmg = new float[5];
}
public boolean newLayer(float time, float damagePerAct){
for(int i=0;i<5;++i){
if(lasting[i]<=0f){
lasting[i]=time;
dmg[i]=damagePerAct;
return true;
}
}
return false;
}
public void burst(){
float damage = 0;
for(int i = 0; i<5; ++i){
damage += Math.max(0, dmg[i])*lasting[i];
}
target.sprite.showStatus(CharSprite.NEGATIVE, "!!!");
if (target.sprite.visible) {
Splash.at( target.sprite.center(), -PointF.PI / 2, PointF.PI / 6,
target.sprite.blood(), 30 );
}
Wound.hit(target);
damage *= Random.Float(1.7f, 2.1f);
target.damage((int)damage, this);
if(target == Dungeon.hero && !target.isAlive()){
Dungeon.fail(getClass());
GLog.n(M.L(this, "burst_die"));
}
detach();
}
public void resetList(){
for(int i=0; i<5; ++i){
lasting[i]=0;
dmg[i]=0;
}
}
public void decreaseTime(){
for(int i=0; i<5; ++i){
lasting[i]-=1f;
}
}
public float oneDamage(){
float damage = 0;
for(int i=0; i<5; ++i){
damage += dmg[i];
}
return damage;
}
public int getLayer(){
int layer = 0;
for(int i=0;i<5;++i){
layer += lasting[i]>0f?1:0;
}
return layer;
}
@Override
public boolean act(){
spend(TICK);
percentDamage += oneDamage();
target.damage((int)percentDamage, this);
if (target.sprite.visible) {
Splash.at( target.sprite.center(), -PointF.PI / 2, PointF.PI / 6,
target.sprite.blood(), Math.min( 10 *(int)percentDamage / target.HT, 10 ) );
}
if(target == Dungeon.hero && !target.isAlive()){
Dungeon.fail(getClass());
GLog.n(M.L(this, "bleed_die"));
}
percentDamage -= (int)percentDamage;
decreaseTime();
if(getLayer()==0) detach();
return true;
}
@Override
public void storeInBundle(Bundle b){
super.storeInBundle(b);
b.put("leftTime", lasting);
b.put("damageLeft", dmg);
b.put("percentDamage", percentDamage);
}
@Override
public void restoreFromBundle(Bundle b){
super.restoreFromBundle(b);
lasting = b.getFloatArray("leftTime");
dmg = b.getFloatArray("damageLeft");
percentDamage = b.getFloat("percentDamage");
}
@Override
public int icon(){
return BuffIndicator.BLEEDING;
}
@Override
public String desc() {
return M.L(this, "desc", getLayer(), oneDamage());
}
@Override
public String toString() {
return M.L(this, "name");
}
@Override
public String heroMessage() {
return M.L(this, "heromsg");
}
@Override
public float iconFadePercent() {
return Math.max(0, (5 - getLayer()) / 5f);
}
}

View File

@ -0,0 +1,36 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.watabou.utils.Bundle;
public class CountBuff extends Buff {
private float count = 0;
public void countUp( float inc ){
count += inc;
}
public void countDown( float inc ){
count -= inc;
}
public float count(){
return count;
}
private static final String COUNT = "count";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(COUNT, count);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
count = bundle.getFloat(COUNT);
}
}

View File

@ -0,0 +1,71 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.watabou.utils.Callback;
//It's called char, but it serves for buffs, not chars.
//Currently DummyChar is only used to hold "level buffs".
//Level buffs are buffs that not affected by chars.
//This kind of buff should be attached to a "static" char that is sealed in level, not existing chars.
//Or it would be problematic if the holder dies, or leaves the level.
//might be better to maintain a dummyChar array. Use id to distinguish between dcs.
public final class DummyChar extends Char {
{
alignment = Alignment.NEUTRAL;
}
@Override
protected boolean act() {
spend(TICK);
if(buffs().isEmpty()){
killDummyChar();
}
return true;
}
@Override
public void damage(int dmg, Object src) {
}
@Override
public boolean canInteract(Char c) {
return false;
}
//return the first DummyChar,
public static DummyChar findDummyChar(){
for(Char ch: Actor.chars()){
if(ch instanceof DummyChar){
return (DummyChar) ch;
}
}
return null;
}
//return existing dummyChar, or create a new dummyChar
public static DummyChar getDC(){
for(Char ch: Actor.chars()){
if(ch instanceof DummyChar){
return (DummyChar) ch;
}
}
DummyChar dc = new DummyChar();
Actor.add(dc);
return dc;
}
//remove existing dummyChar
public static boolean killDummyChar() {
DummyChar dc = findDummyChar();
if (dc == null) {
return false;
}
Actor.remove(dc);
return true;
}
}

View File

@ -0,0 +1,9 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
public class FlavourBuffOpen extends FlavourBuff {
public void setDuration(float time){
spend(time);
}
}

View File

@ -0,0 +1,34 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.noosa.Image;
public class IgnoreArmor extends FlavourBuff {
{
type = buffType.NEUTRAL;
announced = true;
}
@Override
public int icon() {
return BuffIndicator.VULNERABLE;
}
@Override
public void tintIcon(Image icon) {
super.tintIcon(icon);
icon.hardlight(0.8f, 0f, 0f);
}
@Override
public String toString() {
return Messages.get(this, "name");
}
@Override
public String desc() {
return Messages.get(this, "desc", dispTurns());
}
}

View File

@ -0,0 +1,46 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.noosa.Image;
public class PositiveBuffProhibition extends FlavourBuff {
{
type = buffType.NEUTRAL;
announced = true;
}
@Override
public int icon() {
return BuffIndicator.DEGRADE;
}
@Override
public void tintIcon(Image icon) {
super.tintIcon(icon);
icon.hardlight(0f, 1f, 0f);
}
@Override
public String toString() {
return Messages.get(this, "name");
}
@Override
public String desc() {
return Messages.get(this, "desc", dispTurns());
}
@Override
public boolean attachTo(Char target) {
for(Buff b: target.buffs()){
if(b.type == buffType.POSITIVE && !b.revivePersists){
b.detach();
}
}
return super.attachTo(target);
}
}

View File

@ -0,0 +1,33 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.noosa.Image;
public class ZeroAttack extends FlavourBuff {
{
type = buffType.NEUTRAL;
announced = true;
}
@Override
public int icon() {
return BuffIndicator.WEAKNESS;
}
@Override
public void tintIcon(Image icon) {
icon.hardlight(.5f, .5f, .5f);
}
@Override
public String toString() {
return Messages.get(this, "name");
}
@Override
public String desc() {
return Messages.get(this, "desc", dispTurns());
}
}

View File

@ -0,0 +1,33 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.buffs;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.noosa.Image;
public class ZeroDefense extends FlavourBuff {
{
type = buffType.NEUTRAL;
announced = true;
}
@Override
public int icon() {
return BuffIndicator.VULNERABLE;
}
@Override
public void tintIcon(Image icon) {
icon.hardlight(.5f, .5f, .5f);
}
@Override
public String toString() {
return Messages.get(this, "name");
}
@Override
public String desc() {
return Messages.get(this, "desc", dispTurns());
}
}

View File

@ -0,0 +1,154 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.ch;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.RedDragon;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Wandmaker;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.Artifact;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel;
import com.watabou.noosa.Game;
import com.watabou.noosa.Visual;
import com.watabou.utils.Bundle;
public class GameTracker extends Buff {
{
actPriority = VFX_PRIO - 1;
revivePersists = true;
}
private VirtualVisualTimer vvt;
private int maxDepth = -1;
private String allItemInfo = "";
@Override
public boolean act() {
spend(TICK/2f);
if(vvt==null) {
vvt = new VirtualVisualTimer();
Dungeon.hero.sprite.parent.add(vvt);
}
if(!vvt.alive || !vvt.active || vvt.parent == null){
vvt.revive();
vvt.active = true;
Dungeon.hero.sprite.parent.add(vvt);
vvt.parent = Dungeon.hero.sprite.parent;
}
if(maxDepth < Dungeon.depth){
spend(-TICK);
maxDepth = Dungeon.depth;
updateItemInfo();
}
return true;
}
public void onNewLevel(){
if(maxDepth < Dungeon.depth){
spend(-TICK);
maxDepth = Dungeon.depth;
updateItemInfo();
}
}
private void updateItemInfo(){
if(Dungeon.level instanceof RegularLevel){
StringBuilder info = new StringBuilder("");
info.append("dungeon_depth: ").append(maxDepth).append("\n");
for(Heap heap: Dungeon.level.heaps.valueList()){
if((!heap.autoExplored) || heap.type == Heap.Type.CHEST || heap.type == Heap.Type.LOCKED_CHEST || heap.type == Heap.Type.CRYSTAL_CHEST){
for(Item item : heap.items){
appendDesc(item, info, "");
}
}
}
for(Mob m : Dungeon.level.mobs.toArray(new Mob[0])){
if(m instanceof Mimic){
for(Item item: ((Mimic) m).items){
appendDesc(item, info, "MIMIC_HOLD");
}
}
if(m instanceof Ghost){
appendDesc(Ghost.Quest.weapon, info, "QUEST_REWARD");
appendDesc(Ghost.Quest.armor, info, "QUEST_REWARD");
}
if(m instanceof Wandmaker){
appendDesc(Wandmaker.Quest.wand1, info, "QUEST_REWARD");
appendDesc(Wandmaker.Quest.wand2, info, "QUEST_REWARD");
}
if(m instanceof RedDragon){
appendDesc(RedDragon.Quest.weapon, info, "QUEST_REWARD");
appendDesc(RedDragon.Quest.armor, info, "QUEST_REWARD");
}
if(m instanceof Imp){
appendDesc(Imp.Quest.reward, info, "QUEST_REWARD");
}
}
allItemInfo += info.toString();
}
}
private void appendDesc(Item item, StringBuilder info, String prefix){
if(item != null) {
if (
((item instanceof Weapon || item instanceof Armor) && item.level() > 0)
|| (item instanceof Ring || item instanceof Wand || item instanceof Artifact)
) {
String name = item.trueName();
int index = name.indexOf('+');
if(index > 0){
name = name.substring(0, index - 3);
}
info.append(prefix).append(name).append('+').append(item.level()).append(item.cursed ? " CURSED\n" : "\n");
}
}
}
public String itemInfo(){
return allItemInfo;
}
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put("allItemInfo", allItemInfo);
bundle.put("recordedMaxDepth", maxDepth);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
allItemInfo = bundle.getString("allItemInfo");
maxDepth = bundle.getInt("recordedMaxDepth");
}
public static class VirtualVisualTimer extends Visual {
public VirtualVisualTimer(){
super(0, 0, 0, 0);
}
@Override
public void update() {
super.update();
Statistics.second_elapsed += Game.elapsed;
if(Statistics.second_elapsed > 1f){
Statistics.real_seconds += Math.floor(Statistics.second_elapsed);
Statistics.second_elapsed -= Math.floor(Statistics.second_elapsed);
}
if(Statistics.turnsPassed < Statistics.duration){
Statistics.turnsPassed = Statistics.duration;
}else{
Statistics.turnsPassed = Statistics.duration + Actor.now();
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.shatteredpixel.shatteredpixeldungeon.custom.testmode;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import java.util.ArrayList;
public class ImmortalShieldAffecter extends TestItem {
{
image = ItemSpriteSheet.ROUND_SHIELD;
defaultAction = AC_SWITCH;
}
private static final String AC_SWITCH = "switch";
@Override
public ArrayList<String> actions(Hero hero ) {
ArrayList<String> actions = super.actions(hero);
actions.add(AC_SWITCH);
return actions;
}
@Override
public void execute(Hero hero, String action ) {
super.execute(hero, action);
if(action.equals(AC_SWITCH)){
if(isImmortal(hero)){
Buff.detach(hero, ImmortalShield.class);
}else{
Buff.affect(hero, ImmortalShield.class);
}
}
}
private boolean isImmortal(Char target){
return target.buff(ImmortalShield.class)!=null;
}
public static class ImmortalShield extends Buff{
{
type = buffType.NEUTRAL;
announced = false;
revivePersists = true;
}
@Override
public boolean act(){
spend(TICK);
return true;
}
@Override
public void fx(boolean on) {
if (on) target.sprite.add(CharSprite.State.SHIELDED);
else target.sprite.remove(CharSprite.State.SHIELDED);
}
}
}

View File

@ -75,6 +75,8 @@ public class Heap implements Bundlable {
public boolean seen = false; public boolean seen = false;
public boolean haunted = false; public boolean haunted = false;
public boolean autoExplored = false; //used to determine if this heap should count for exploration bonus
public LinkedList<Item> items = new LinkedList<>(); public LinkedList<Item> items = new LinkedList<>();
public void open( Hero hero ) { public void open( Hero hero ) {
@ -408,6 +410,8 @@ public class Heap implements Bundlable {
private static final String ITEMS = "items"; private static final String ITEMS = "items";
private static final String HAUNTED = "haunted"; private static final String HAUNTED = "haunted";
private static final String AUTO_EXPLORED = "auto_explored";
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void restoreFromBundle( Bundle bundle ) { public void restoreFromBundle( Bundle bundle ) {
@ -432,6 +436,9 @@ public class Heap implements Bundlable {
haunted = bundle.getBoolean( HAUNTED ); haunted = bundle.getBoolean( HAUNTED );
//SPD
autoExplored = bundle.getBoolean( AUTO_EXPLORED );
} }
@Override @Override
@ -441,6 +448,9 @@ public class Heap implements Bundlable {
bundle.put( TYPE, type.toString() ); bundle.put( TYPE, type.toString() );
bundle.put( ITEMS, items ); bundle.put( ITEMS, items );
bundle.put( HAUNTED, haunted ); bundle.put( HAUNTED, haunted );
//SPD
bundle.put( AUTO_EXPLORED, autoExplored );
} }
} }

View File

@ -26,18 +26,51 @@ import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Chill;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corrosion;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Doom;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Dread;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Drowsy;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LockedFloor; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LockedFloor;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicalSleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ScrollEmpower; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ScrollEmpower;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Slow;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SoulMark; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SoulMark;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vulnerable;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Weakness;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.WildMagic; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.WildMagic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bee;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Piranha;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Statue;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Swarm;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Wraith;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile; import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight;
@ -61,6 +94,7 @@ import com.watabou.utils.PointF;
import com.watabou.utils.Random; import com.watabou.utils.Random;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
public abstract class Wand extends Item { public abstract class Wand extends Item {
public static final String AC_ZAP = "ZAP"; public static final String AC_ZAP = "ZAP";
@ -381,7 +415,7 @@ public abstract class Wand extends Item {
particle.radiateXY(0.5f); particle.radiateXY(0.5f);
} }
protected void wandUsed() { public void wandUsed() {
if (!isIdentified()) { if (!isIdentified()) {
float uses = Math.min( availableUsesToID, Talent.itemIDSpeedFactor(Dungeon.hero, this) ); float uses = Math.min( availableUsesToID, Talent.itemIDSpeedFactor(Dungeon.hero, this) );
availableUsesToID -= uses; availableUsesToID -= uses;
@ -531,6 +565,150 @@ public abstract class Wand extends Item {
return collisionProperties; return collisionProperties;
} }
//浊焰
private static final float MINOR_DEBUFF_WEAKEN = 1/4f;
private static final HashMap<Class<? extends Buff>, Float> MINOR_DEBUFFS = new HashMap<>();
static{
MINOR_DEBUFFS.put(Weakness.class, 2f);
MINOR_DEBUFFS.put(Vulnerable.class, 2f);
MINOR_DEBUFFS.put(Cripple.class, 1f);
MINOR_DEBUFFS.put(Blindness.class, 1f);
MINOR_DEBUFFS.put(Terror.class, 1f);
MINOR_DEBUFFS.put(Chill.class, 0f);
MINOR_DEBUFFS.put(Ooze.class, 0f);
MINOR_DEBUFFS.put(Roots.class, 0f);
MINOR_DEBUFFS.put(Vertigo.class, 0f);
MINOR_DEBUFFS.put(Drowsy.class, 0f);
MINOR_DEBUFFS.put(Bleeding.class, 0f);
MINOR_DEBUFFS.put(Burning.class, 0f);
MINOR_DEBUFFS.put(Poison.class, 0f);
}
private static final float MAJOR_DEBUFF_WEAKEN = 1/2f;
private static final HashMap<Class<? extends Buff>, Float> MAJOR_DEBUFFS = new HashMap<>();
static{
MAJOR_DEBUFFS.put(Amok.class, 3f);
MAJOR_DEBUFFS.put(Slow.class, 2f);
MAJOR_DEBUFFS.put(Hex.class, 2f);
MAJOR_DEBUFFS.put(Paralysis.class, 1f);
MAJOR_DEBUFFS.put(Dread.class, 0f);
MAJOR_DEBUFFS.put(Charm.class, 0f);
MAJOR_DEBUFFS.put(MagicalSleep.class, 0f);
MAJOR_DEBUFFS.put(SoulMark.class, 0f);
MAJOR_DEBUFFS.put(Corrosion.class, 0f);
MAJOR_DEBUFFS.put(Frost.class, 0f);
MAJOR_DEBUFFS.put(Doom.class, 0f);
}
public void onZapX(Ballistica bolt) {
Char ch = Actor.findChar(bolt.collisionPos);
if (ch != null){
if (!(ch instanceof Mob)){
return;
}
Mob enemy = (Mob) ch;
float corruptingPower = 3 + buffedLvl()/2f;
//base enemy resistance is usually based on their exp, but in special cases it is based on other criteria
float enemyResist = 1 + enemy.EXP;
if (ch instanceof Mimic || ch instanceof Statue){
enemyResist = 1 + Dungeon.depth;
} else if (ch instanceof Piranha || ch instanceof Bee) {
enemyResist = 1 + Dungeon.depth/2f;
} else if (ch instanceof Wraith) {
//divide by 5 as wraiths are always at full HP and are therefore ~5x harder to corrupt
enemyResist = (1f + Dungeon.depth/3f) / 5f;
} else if (ch instanceof Swarm){
//child swarms don't give exp, so we force this here.
enemyResist = 1 + 3;
}
//100% health: 5x resist 75%: 3.25x resist 50%: 2x resist 25%: 1.25x resist
enemyResist *= 1 + 4*Math.pow(enemy.HP/(float)enemy.HT, 2);
//debuffs placed on the enemy reduce their resistance
for (Buff buff : enemy.buffs()){
if (MAJOR_DEBUFFS.containsKey(buff.getClass())) enemyResist *= (1f-MAJOR_DEBUFF_WEAKEN);
else if (MINOR_DEBUFFS.containsKey(buff.getClass())) enemyResist *= (1f-MINOR_DEBUFF_WEAKEN);
else if (buff.type == Buff.buffType.NEGATIVE) enemyResist *= (1f-MINOR_DEBUFF_WEAKEN);
}
//cannot re-corrupt or doom an enemy, so give them a major debuff instead
if(enemy.buff(Corruption.class) != null || enemy.buff(Doom.class) != null){
corruptingPower = enemyResist - 0.001f;
}
if (corruptingPower > enemyResist){
corruptEnemy( enemy );
} else {
float debuffChance = corruptingPower / enemyResist;
if (Random.Float() < debuffChance){
debuffEnemy( enemy, MAJOR_DEBUFFS);
} else {
debuffEnemy( enemy, MINOR_DEBUFFS);
}
}
wandProc(ch, chargesPerCast());
Sample.INSTANCE.play( Assets.Sounds.HIT_MAGIC, 1, 0.8f * Random.Float(0.87f, 1.15f) );
} else {
Dungeon.level.pressCell(bolt.collisionPos);
}
}
private void debuffEnemy( Mob enemy, HashMap<Class<? extends Buff>, Float> category ){
//do not consider buffs which are already assigned, or that the enemy is immune to.
HashMap<Class<? extends Buff>, Float> debuffs = new HashMap<>(category);
for (Buff existing : enemy.buffs()){
if (debuffs.containsKey(existing.getClass())) {
debuffs.put(existing.getClass(), 0f);
}
}
for (Class<?extends Buff> toAssign : debuffs.keySet()){
if (debuffs.get(toAssign) > 0 && enemy.isImmune(toAssign)){
debuffs.put(toAssign, 0f);
}
}
//all buffs with a > 0 chance are flavor buffs
Class<?extends FlavourBuff> debuffCls = (Class<? extends FlavourBuff>) Random.chances(debuffs);
if (debuffCls != null){
Buff.append(enemy, debuffCls, 6 + buffedLvl()*3);
} else {
//if no debuff can be applied (all are present), then go up one tier
if (category == MINOR_DEBUFFS) debuffEnemy( enemy, MAJOR_DEBUFFS);
else if (category == MAJOR_DEBUFFS) corruptEnemy( enemy );
}
}
private void corruptEnemy( Mob enemy ){
//cannot re-corrupt or doom an enemy, so give them a major debuff instead
if(enemy.buff(Corruption.class) != null || enemy.buff(Doom.class) != null){
GLog.w( Messages.get(this, "already_corrupted") );
return;
}
if (!enemy.isImmune(Corruption.class)){
Corruption.corruptionHeal(enemy);
AllyBuff.affectAndLoot(enemy, curUser, Corruption.class);
} else {
Buff.affect(enemy, Doom.class);
}
}
public static class PlaceHolder extends Wand { public static class PlaceHolder extends Wand {
{ {

View File

@ -28,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.effects.SpellSprite; import com.shatteredpixel.shatteredpixeldungeon.effects.SpellSprite;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.EndingBlade;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
@ -103,6 +104,7 @@ public class WandOfMagicMissile extends DamageWand {
private int level = 0; private int level = 0;
private Wand wandJustApplied; //we don't bundle this as it's only used right as the buff is applied private Wand wandJustApplied; //we don't bundle this as it's only used right as the buff is applied
private EndingBlade wandJustAppliedX; //we don't bundle this as it's only used right as the buff is applied
public void setup(Wand wand){ public void setup(Wand wand){
if (level < wand.buffedLvl()){ if (level < wand.buffedLvl()){
@ -128,6 +130,12 @@ public class WandOfMagicMissile extends DamageWand {
return result; return result;
} }
public EndingBlade wandJustAppliedX(){
EndingBlade result = this.wandJustAppliedX;
this.wandJustAppliedX = null;
return result;
}
@Override @Override
public int icon() { public int icon() {
return BuffIndicator.UPGRADE; return BuffIndicator.UPGRADE;

View File

@ -1,23 +1,81 @@
package com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee; package com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Chill;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corrosion;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Doom;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Drowsy;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicalSleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ScrollEmpower;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Slow;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SoulMark;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Weakness;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bee;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Piranha;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Statue;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Swarm;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Wraith;
import com.shatteredpixel.shatteredpixeldungeon.custom.utils.BallisticaReal;
import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PurpleParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight;
import com.shatteredpixel.shatteredpixeldungeon.items.bombs.LaserPython; import com.shatteredpixel.shatteredpixeldungeon.items.bombs.LaserPython;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRecharging;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfMagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blazing; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blazing;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Kinetic; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Kinetic;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Shocking; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Shocking;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Unstable; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Unstable;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.CellSelector;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Callback;
import com.watabou.utils.Point;
import com.watabou.utils.PointF;
import com.watabou.utils.Random;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
public class EndingBlade extends Weapon { public class EndingBlade extends Weapon {
{ {
image = ItemSpriteSheet.ENDDIED; image = ItemSpriteSheet.ENDDIED;
tier = 4; tier = 4;
@ -51,12 +109,14 @@ public class EndingBlade extends Weapon {
private static final String FIRST = "first"; private static final String FIRST = "first";
private static final String INTRPS = "intrps"; private static final String INTRPS = "intrps";
private static final String TDM = "shocking_ordinals";
@Override @Override
public void storeInBundle(Bundle bundle) { public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle); super.storeInBundle(bundle);
bundle.put(FIRST, firstx); bundle.put(FIRST, firstx);
bundle.put(INTRPS, fireenergy); bundle.put(INTRPS, fireenergy);
bundle.put(TDM, TIME_TO_DIED);
} }
@Override @Override
@ -64,10 +124,11 @@ public class EndingBlade extends Weapon {
super.restoreFromBundle(bundle); super.restoreFromBundle(bundle);
firstx = bundle.getBoolean(FIRST); firstx = bundle.getBoolean(FIRST);
fireenergy = bundle.getInt(INTRPS); fireenergy = bundle.getInt(INTRPS);
TIME_TO_DIED = bundle.getInt(TDM);
} }
public String desc() { public String desc() {
return Messages.get(this, "desc")+"_"+fireenergy+"_"; return Messages.get(this, "desc")+"_"+fireenergy+"_"+Messages.get(this, "desc_2")+"_"+TIME_TO_DIED+"_";
} }
//每100点浊焰能量自动升级 //每100点浊焰能量自动升级
@ -75,7 +136,7 @@ public class EndingBlade extends Weapon {
public int level() { public int level() {
return fireenergy/100; return fireenergy/100;
} }
private int TIME_TO_DIED = 0;
public void execute( Hero hero, String action ) { public void execute( Hero hero, String action ) {
super.execute( hero, action ); super.execute( hero, action );
@ -92,7 +153,16 @@ public class EndingBlade extends Weapon {
} }
break; break;
case AC_DIEDGHOST: case AC_DIEDGHOST:
GLog.p("2"); if(level >= 5 && TIME_TO_DIED == 0) {
curUser = hero;
curItem = this;
GameScene.selectCell(zapper);
TIME_TO_DIED = 20;
} else if (TIME_TO_DIED != 0) {
GLog.n("道具正在冷却");
} else {
GLog.n("等级不足");
}
break; break;
case AC_HEALRESET: case AC_HEALRESET:
GLog.w("3"); GLog.w("3");
@ -100,8 +170,508 @@ public class EndingBlade extends Weapon {
} }
} }
public int maxCharges = initialCharges();
public int curCharges = maxCharges;
public int collisionProperties(int target){
return collisionProperties;
}
protected int collisionProperties = Ballistica.MAGIC_BOLT;
protected int initialCharges() {
return 2;
}
protected static CellSelector.Listener zapper = new CellSelector.Listener() {
@Override
public void onSelect( Integer target ) {
if (target != null) {
//FIXME this safety check shouldn't be necessary
//it would be better to eliminate the curItem static variable.
final EndingBlade curWand;
if (curItem instanceof EndingBlade) {
curWand = (EndingBlade) EndingBlade.curItem;
} else {
return;
}
final Ballistica shot = new Ballistica( curUser.pos, target, curWand.collisionProperties(target));
int cell = shot.collisionPos;
if (target == curUser.pos || cell == curUser.pos) {
if (target == curUser.pos && curUser.hasTalent(Talent.SHIELD_BATTERY)){
float shield = curUser.HT * (0.04f*curWand.curCharges);
if (curUser.pointsInTalent(Talent.SHIELD_BATTERY) == 2) shield *= 1.5f;
Buff.affect(curUser, Barrier.class).setShield(Math.round(shield));
curWand.curCharges = 0;
curUser.sprite.operate(curUser.pos);
Sample.INSTANCE.play(Assets.Sounds.CHARGEUP);
ScrollOfRecharging.charge(curUser);
updateQuickslot();
curUser.spend(Actor.TICK);
return;
}
GLog.i( Messages.get(Wand.class, "self_target") );
return;
}
curUser.sprite.zap(cell);
//attempts to target the cell aimed at if something is there, otherwise targets the collision pos.
if (Actor.findChar(target) != null)
QuickSlotButton.target(Actor.findChar(target));
else
QuickSlotButton.target(Actor.findChar(cell));
if (curWand.tryToZap(curUser, target)) {
curUser.busy();
curWand.fxs(shot, new Callback() {
public void call() {
curWand.onZap(shot);
curWand.wandUsed();
}
});
curWand.cursedKnown = true;
}
}
}
@Override
public String prompt() {
return Messages.get(Wand.class, "prompt");
}
};
public boolean tryToZap( Hero owner, int target ){
if (owner.buff(MagicImmune.class) != null){
GLog.w( Messages.get(Wand.class, "no_magic") );
return false;
}
return true;
}
protected Wand.Charger charger;
private static final int USES_TO_ID = 10;
private float usesLeftToID = USES_TO_ID;
private float availableUsesToID = USES_TO_ID/2f;
private static final float TIME_TO_ZAP = 1f;
public void wandUsed() {
if (!isIdentified()) {
float uses = Math.min( availableUsesToID, Talent.itemIDSpeedFactor(Dungeon.hero, this) );
availableUsesToID -= uses;
usesLeftToID -= uses;
if (usesLeftToID <= 0 || Dungeon.hero.pointsInTalent(Talent.SCHOLARS_INTUITION) == 2) {
identify();
GLog.p( Messages.get(Wand.class, "identify") );
Badges.validateItemLevelAquired( this );
}
}
curCharges -= cursed ? 1 : chargesPerCast();
//remove magic charge at a higher priority, if we are benefiting from it are and not the
//wand that just applied it
WandOfMagicMissile.MagicCharge buff = curUser.buff(WandOfMagicMissile.MagicCharge.class);
if (buff != null
&& buff.wandJustAppliedX() != this
&& buff.level() == buffedLvl()
&& buffedLvl() > super.buffedLvl()){
buff.detach();
} else {
ScrollEmpower empower = curUser.buff(ScrollEmpower.class);
if (empower != null){
empower.use();
}
}
//if the wand is owned by the hero, but not in their inventory, it must be in the staff
if (charger != null
&& charger.target == Dungeon.hero
&& !Dungeon.hero.belongings.contains(this)) {
if (curCharges == 0 && Dungeon.hero.hasTalent(Talent.BACKUP_BARRIER)) {
//grants 3/5 shielding
Buff.affect(Dungeon.hero, Barrier.class).setShield(1 + 2 * Dungeon.hero.pointsInTalent(Talent.BACKUP_BARRIER));
}
if (Dungeon.hero.hasTalent(Talent.EMPOWERED_STRIKE)){
Buff.prolong(Dungeon.hero, Talent.EmpoweredStrikeTracker.class, 10f);
}
}
Invisibility.dispel();
updateQuickslot();
curUser.spendAndNext( TIME_TO_ZAP );
}
protected int nReflections(int level){
//return Math.min(2+level/3, 5);
return 5;
}
protected float reflectionDamageFactor(int reflections){
return 1f+0.1f*reflections*reflections;
}
public void onZap( Ballistica beam ) {
int count = 0;
for(BallisticaReal b: beams){
onZapX(b, count);
++count;
}
Char ch = Actor.findChar(beam.collisionPos);
if (ch != null){
if (!(ch instanceof Mob)){
return;
}
Mob enemy = (Mob) ch;
float corruptingPower = 3 + buffedLvl()/2f;
//base enemy resistance is usually based on their exp, but in special cases it is based on other criteria
float enemyResist = 1 + enemy.EXP;
if (ch instanceof Mimic || ch instanceof Statue){
enemyResist = 1 + Dungeon.depth;
} else if (ch instanceof Piranha || ch instanceof Bee) {
enemyResist = 1 + Dungeon.depth/2f;
} else if (ch instanceof Wraith) {
//divide by 5 as wraiths are always at full HP and are therefore ~5x harder to corrupt
enemyResist = (1f + Dungeon.depth/3f) / 5f;
} else if (ch instanceof Swarm){
//child swarms don't give exp, so we force this here.
enemyResist = 1 + 3;
}
//100% health: 5x resist 75%: 3.25x resist 50%: 2x resist 25%: 1.25x resist
enemyResist *= 1 + 4*Math.pow(enemy.HP/(float)enemy.HT, 2);
//debuffs placed on the enemy reduce their resistance
for (Buff buff : enemy.buffs()){
if (MAJOR_DEBUFFS.containsKey(buff.getClass())) enemyResist *= (1f-MAJOR_DEBUFF_WEAKEN);
else if (MINOR_DEBUFFS.containsKey(buff.getClass())) enemyResist *= (1f-MINOR_DEBUFF_WEAKEN);
else if (buff.type == Buff.buffType.NEGATIVE) enemyResist *= (1f-MINOR_DEBUFF_WEAKEN);
}
//cannot re-corrupt or doom an enemy, so give them a major debuff instead
if(enemy.buff(Corruption.class) != null || enemy.buff(Doom.class) != null){
corruptingPower = enemyResist - 0.001f;
}
if (corruptingPower > enemyResist){
corruptEnemy( enemy );
} else {
float debuffChance = corruptingPower / enemyResist;
if (Random.Float() < debuffChance){
debuffEnemy( enemy, MAJOR_DEBUFFS);
} else {
debuffEnemy( enemy, MINOR_DEBUFFS);
}
}
wandProc(ch, chargesPerCast());
Sample.INSTANCE.play( Assets.Sounds.HIT_MAGIC, 1, 0.8f * Random.Float(0.87f, 1.15f) );
}
}
public void onZapX(BallisticaReal beam,int reflection) {
int level = buffedLvl();
int maxDistance = beam.dist;
ArrayList<Char> chars = new ArrayList<>();
for (int c : beam.subPath((reflection>0?0:1), maxDistance)) {
//prevent self_damage
if(c==beam.sourceI && c==curUser.pos) continue;
Char ch;
if ((ch = Actor.findChar( c )) != null) {
chars.add( ch );
}
if (Dungeon.level.flamable[c]) {
Dungeon.level.destroy( c );
GameScene.updateMap( c );
}
CellEmitter.center( c ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) );
}
Dungeon.observe();
boolean alive = curUser.isAlive();
int lvl = level;
for (Char ch : chars) {
wandProc(ch, chargesPerCast());
int damage = Math.round(2*reflectionDamageFactor(reflection));
if(!ch.equals(curUser)) {
ch.damage(damage, this);
}else{
ch.damage(damage/6, this);
}
ch.sprite.centerEmitter().burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) );
ch.sprite.flash();
}
if (!curUser.isAlive() && alive) {
Dungeon.fail( getClass() );
GLog.n(Messages.get(this, "ondeath"));
}
}
protected int chargesPerCast() {
return 2;
}
protected void wandProc(Char target, int chargesUsed){
wandProc(target, buffedLvl(), chargesUsed);
}
//TODO Consider externalizing char awareness buff
protected static void wandProc(Char target, int wandLevel, int chargesUsed){
if (Dungeon.hero.hasTalent(Talent.ARCANE_VISION)) {
int dur = 5 + 5*Dungeon.hero.pointsInTalent(Talent.ARCANE_VISION);
Buff.append(Dungeon.hero, TalismanOfForesight.CharAwareness.class, dur).charID = target.id();
}
if (target != Dungeon.hero &&
Dungeon.hero.subClass == HeroSubClass.WARLOCK &&
//standard 1 - 0.92^x chance, plus 7%. Starts at 15%
Random.Float() > (Math.pow(0.92f, (wandLevel*chargesUsed)+1) - 0.07f)){
SoulMark.prolong(target, SoulMark.class, SoulMark.DURATION + wandLevel);
}
}
private static final float MINOR_DEBUFF_WEAKEN = 1/4f;
private static final HashMap<Class<? extends Buff>, Float> MINOR_DEBUFFS = new HashMap<>();
static{
MINOR_DEBUFFS.put(Weakness.class, 2f);
MINOR_DEBUFFS.put(Corruption.class, 2f);
MINOR_DEBUFFS.put(Cripple.class, 1f);
MINOR_DEBUFFS.put(Blindness.class, 1f);
MINOR_DEBUFFS.put(Terror.class, 1f);
MINOR_DEBUFFS.put(Chill.class, 0f);
MINOR_DEBUFFS.put(Ooze.class, 0f);
MINOR_DEBUFFS.put(Corruption.class, 4f);
MINOR_DEBUFFS.put(Vertigo.class, 0f);
MINOR_DEBUFFS.put(Drowsy.class, 0f);
MINOR_DEBUFFS.put(Corruption.class, 10f);
MINOR_DEBUFFS.put(Burning.class, 0f);
MINOR_DEBUFFS.put(Poison.class, 0f);
}
private static final float MAJOR_DEBUFF_WEAKEN = 1/2f;
private static final HashMap<Class<? extends Buff>, Float> MAJOR_DEBUFFS = new HashMap<>();
static{
MAJOR_DEBUFFS.put(Corruption.class, 3f);
MAJOR_DEBUFFS.put(Slow.class, 2f);
MAJOR_DEBUFFS.put(Hex.class, 2f);
MAJOR_DEBUFFS.put(Paralysis.class, 1f);
MAJOR_DEBUFFS.put(Corruption.class, 0f);
MAJOR_DEBUFFS.put(Corruption.class, 0f);
MAJOR_DEBUFFS.put(MagicalSleep.class, 0f);
MAJOR_DEBUFFS.put(SoulMark.class, 0f);
MAJOR_DEBUFFS.put(Corrosion.class, 0f);
MAJOR_DEBUFFS.put(Frost.class, 0f);
MAJOR_DEBUFFS.put(Doom.class, 0f);
}
private void debuffEnemy( Mob enemy, HashMap<Class<? extends Buff>, Float> category ){
//do not consider buffs which are already assigned, or that the enemy is immune to.
HashMap<Class<? extends Buff>, Float> debuffs = new HashMap<>(category);
for (Buff existing : enemy.buffs()){
if (debuffs.containsKey(existing.getClass())) {
debuffs.put(existing.getClass(), 0f);
}
}
for (Class<?extends Buff> toAssign : debuffs.keySet()){
if (debuffs.get(toAssign) > 0 && enemy.isImmune(toAssign)){
debuffs.put(toAssign, 0f);
}
}
//all buffs with a > 0 chance are flavor buffs
Class<?extends FlavourBuff> debuffCls = (Class<? extends FlavourBuff>) Random.chances(debuffs);
if (debuffCls != null){
Buff.append(enemy, debuffCls, 6 + buffedLvl()*3);
} else {
//if no debuff can be applied (all are present), then go up one tier
if (category == MINOR_DEBUFFS) debuffEnemy( enemy, MAJOR_DEBUFFS);
else if (category == MAJOR_DEBUFFS) corruptEnemy( enemy );
}
}
private void corruptEnemy( Mob enemy ){
//cannot re-corrupt or doom an enemy, so give them a major debuff instead
if(enemy.buff(Corruption.class) != null || enemy.buff(Doom.class) != null){
GLog.w( Messages.get(this, "already_corrupted") );
return;
}
if (!enemy.isImmune(Corruption.class)){
Corruption.corruptionHeal(enemy);
AllyBuff.affectAndLoot(enemy, curUser, Corruption.class);
} else {
Buff.affect(enemy, Doom.class);
}
}
protected static ArrayList<PointF> fxS = new ArrayList<>();
protected static ArrayList<PointF> fxE = new ArrayList<>();
public void fxs(Ballistica beam, Callback callback) {
MagicMissile.boltFromChar( curUser.sprite.parent,
MagicMissile.SHADOW,
curUser.sprite,
beam.collisionPos,
callback);
Sample.INSTANCE.play( Assets.Sounds.ZAP );
buildBeams(beam);
int size = fxE.size();
for(int i=0;i<size;++i){
//Point p = Dungeon.level.cellToPoint(fxE.get(i));
//GLog.i("(%d, %d)", p.x, p.y);
curUser.sprite.parent.add(new Beam.DeathRayS(BallisticaReal.raisedPointToScreen(fxS.get(i)),
BallisticaReal.raisedPointToScreen(fxE.get(i)) ));
}
callback.call();
}
protected static ArrayList<BallisticaReal> beams = new ArrayList<>();
protected static ArrayList<Float> offset_1 = new ArrayList<>();
protected void buildOffsetArray(int level){
offset_1.clear();
float offPerStep = 45f* (1.9f+level) / (1.9f+level*3f);
int maxDSB;
if(level>7){maxDSB = 0;}
else if(level>3){maxDSB = 0;}
else{maxDSB = 0;}
//offset_1.add(0f);
if(maxDSB>0) {
for (int i = -maxDSB; i < maxDSB + 1; ++i) {
offset_1.add(i * offPerStep / maxDSB);
}
}else{
offset_1.add(0f);
}
// if(level<9) return base_3;
//else return base_4;
}
protected PointF pointToF(Point p){
return new PointF(p.x, p.y);
}
protected void addBeam(BallisticaReal beam){
beams.add(beam);
fxS.add(beam.sourceF);
fxE.add(beam.collisionF);
}
protected void buildBeams(Ballistica beam){
fxS.clear();
fxE.clear();
beams.clear();
buildOffsetArray(this.level());
//addBeam(new Ballistica(beam.sourcePos, beam.collisionPos, Ballistica.STOP_SOLID | Ballistica.IGNORE_SOFT_SOLID));
float angle = PointF.angle(new PointF(pointToF(Dungeon.level.cellToPoint(beam.sourcePos))),
new PointF(pointToF(Dungeon.level.cellToPoint(beam.collisionPos))));
angle /= PointF.G2R;
if(angle<0f) angle += 360f;
//GLog.i("%f,", angle);
int scatter = offset_1.size();
for(int i=0;i<scatter;++i){
addBeam(new BallisticaReal(beam.sourcePos, angle + offset_1.get(i), 20, BallisticaReal.STOP_SOLID | BallisticaReal.IGNORE_SOFT_SOLID));
}
int maxRf = nReflections(this.level());
for(int ref = 0; ref < maxRf; ++ref) {
for (int i = 0; i < scatter; ++i) {
BallisticaReal br = new BallisticaReal(fxE.get(i+ref*scatter), reflectAngle(fxS.get(i+ref*scatter), fxE.get(i+ref*scatter)), 20, BallisticaReal.STOP_SOLID | BallisticaReal.IGNORE_SOFT_SOLID);
addBeam(br);
}
}
}
protected float horizontalReflectAngle(float angle){
angle = angle%360f;
return 360f-angle;
}
protected float verticalReflectAngle(float angle){
angle = angle%360f;
if(angle<180f) angle=180f-angle;
else angle = 540f-angle;
return angle;
}
protected float reflectAngle(PointF s, PointF e){
//PointF realPoint = nextPF(s,e);
float angle = PointF.angle(s,e)/PointF.G2R;
if(angle<0f) angle+= 360f;
float dx = e.x - s.x;
float dy = e.y - s.y;
boolean up = dy>0;
boolean right = dx>0;
boolean horizontalWall = false;
boolean verticalWall = false;
int xTile=(int)e.x;
if(e.x-(int)e.x<1e-5 && right){
xTile-=1;
}
int yTile=(int)e.y;
if(e.y-(int)e.y<1e-5 && up){
yTile-=1;
}
final int[] neigh = new int[]{-1, 1, -Dungeon.level.width(), Dungeon.level.width()};
boolean[] isWall = new boolean[4];
for(int i=0; i<4; ++i){
isWall[i]=Dungeon.level.solid[xTile+yTile*Dungeon.level.width()+neigh[i]];
}
if(e.x-(int)e.x<1e-5){
verticalWall = (right && isWall[1]) || (!right && isWall[0]);
}
if(e.y-(int)e.y<1e-5){
horizontalWall = (up && isWall[3]) || (!up && isWall[2]);
}
if(horizontalWall != verticalWall){
if(horizontalWall) return horizontalReflectAngle(angle);
else return verticalReflectAngle(angle);
}else if(horizontalWall && verticalWall){
if(Math.abs(dx)<Math.abs(dy)){
return horizontalReflectAngle(angle);
}else{
return verticalReflectAngle(angle);
}
}else{
return angle;
}
}
@Override @Override
public int STRReq(int C) { public int STRReq(int C) {
@ -112,6 +682,12 @@ public class EndingBlade extends Weapon {
//常规加+1浊焰能量 //常规加+1浊焰能量
++fireenergy; ++fireenergy;
int dmg; int dmg;
tier+= fireenergy/100;
if(TIME_TO_DIED != 0){
TIME_TO_DIED--;
}
if(level >= 10){ if(level >= 10){
fireenergy += 0; fireenergy += 0;
//武器最高级 //武器最高级
@ -168,14 +744,16 @@ public class EndingBlade extends Weapon {
String info = desc(); String info = desc();
if (levelKnown) { if (levelKnown) {
info += "\n\n" + Messages.get(MeleeWeapon.class, "stats_known", tier, augment.damageFactor(min()), augment.damageFactor(max()), STRReq()); info += "\n\n" + Messages.get(MeleeWeapon.class, "stats_known", 4+fireenergy/100,
augment.damageFactor(min()),
augment.damageFactor(max()), STRReq());
if (STRReq() > Dungeon.hero.STR()) { if (STRReq() > Dungeon.hero.STR()) {
info += " " + Messages.get(Weapon.class, "too_heavy"); info += " " + Messages.get(Weapon.class, "too_heavy");
} else if (Dungeon.hero.STR() > STRReq()){ } else if (Dungeon.hero.STR() > STRReq()){
info += " " + Messages.get(Weapon.class, "excess_str", Dungeon.hero.STR() - STRReq()); info += " " + Messages.get(Weapon.class, "excess_str", Dungeon.hero.STR() - STRReq());
} }
} else { } else {
info += "\n\n" + Messages.get(MeleeWeapon.class, "stats_unknown", tier, min(0), max(0), STRReq(0)); info += "\n\n" + Messages.get(MeleeWeapon.class, "stats_unknown", 4+fireenergy/100, min(0), max(0), STRReq(0));
if (STRReq(0) > Dungeon.hero.STR()) { if (STRReq(0) > Dungeon.hero.STR()) {
info += " " + Messages.get(MeleeWeapon.class, "probably_too_heavy"); info += " " + Messages.get(MeleeWeapon.class, "probably_too_heavy");
} }

View File

@ -110,6 +110,10 @@ public abstract class Level implements Bundlable {
SECRETS SECRETS
} }
public boolean isLevelExplored( int depth ){
return false;
}
protected int width; protected int width;
protected int height; protected int height;
protected int length; protected int length;

View File

@ -29,12 +29,15 @@ import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SacrificialFire;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.RandomBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.RandomBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GoldenMimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GoldenMimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Statue;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
@ -43,13 +46,16 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.food.SmallRation; import com.shatteredpixel.shatteredpixeldungeon.items.food.SmallRation;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage; import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.GoldenKey; import com.shatteredpixel.shatteredpixeldungeon.items.keys.GoldenKey;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.builders.Builder; import com.shatteredpixel.shatteredpixeldungeon.levels.builders.Builder;
import com.shatteredpixel.shatteredpixeldungeon.levels.builders.FigureEightBuilder; import com.shatteredpixel.shatteredpixeldungeon.levels.builders.FigureEightBuilder;
import com.shatteredpixel.shatteredpixeldungeon.levels.builders.LoopBuilder; import com.shatteredpixel.shatteredpixeldungeon.levels.builders.LoopBuilder;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter; import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.MagicalFireRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.NxhyShopRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.NxhyShopRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.NyzBombAndBooksRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.NyzBombAndBooksRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.PitRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.PitRoom;
@ -84,6 +90,57 @@ public abstract class RegularLevel extends Level {
protected Room roomEntrance; protected Room roomEntrance;
protected Room roomExit; protected Room roomExit;
@Override
public boolean isLevelExplored( int depth ) {
//A level is considered fully explored if:
//There are no levelgen heaps which are undiscovered, in an openable container, or which contain keys
for (Heap h : heaps.valueList()){
if (h.autoExplored) continue;
if (!h.seen || (h.type != Heap.Type.HEAP && h.type != Heap.Type.FOR_SALE && h.type != Heap.Type.CRYSTAL_CHEST)){
return false;
}
for (Item i : h.items){
if (i instanceof Key){
return false;
}
}
}
//There is no magical fire or sacrificial fire
for (Blob b : blobs.values()){
if (b.volume > 0 && (b instanceof MagicalFireRoom.EternalFire || b instanceof SacrificialFire)){
return false;
}
}
//There are no statues or mimics (unless they were made allies)
for (Mob m : mobs.toArray(new Mob[0])){
if (m.alignment != Char.Alignment.ALLY && (m instanceof Statue || m instanceof Mimic)){
return false;
}
}
//There are no barricades, locked doors, or hidden doors
for (int i = 0; i < length; i++){
if (map[i] == Terrain.BARRICADE || map[i] == Terrain.LOCKED_DOOR || map[i] == Terrain.SECRET_DOOR){
return false;
}
}
//There are no unused keys for this depth in the journal
for (Notes.KeyRecord rec : Notes.getRecords(Notes.KeyRecord.class)){
if (rec.depth() == depth){
return false;
}
}
//Note that it is NOT required for the player to see every tile or discover every trap.
return true;
}
@Override @Override
protected boolean build() { protected boolean build() {

View File

@ -0,0 +1,329 @@
package com.shatteredpixel.shatteredpixeldungeon.mechanics;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
import com.watabou.utils.Point;
import com.watabou.utils.PointF;
import java.util.ArrayList;
import java.util.List;
//REAL ballistica means works like real;
//Normally ballistica in game can go through corners when the beam go through a wall
//but the tile of wall is not built in trace. (Because there is only one tile for each tile in main direction)
//However, REAL ballistica can't go through corners.
//It considers all the points the beam cross with each x and y integer
//So complex actions like reflection can work correctly.
//Walls, target cells, chars are considered as a whole tile. Even 0.001 tile collision would stop.
//Yeah it would be quite odd when this happens, but it works for now.
//Integer pos is considered at the center of tile.
public class BallisticaReal {
//Why here we do NOT record FULL path?
//first, introduction of range makes real collision quite messy if we add end point to path.
//second, real beam collides with boundary, which means it has already considered the next cell.
//if we want to control the full path, just build a never-stop one and go by tiles.
//third, it would cost much more if go through full path. There are cases when it updates each flash.
public ArrayList<Integer> pathI = new ArrayList<>();
public Integer sourceI = null;
public Integer collisionI = null;
public Integer dist = 0;
public ArrayList<PointF> pathF = new ArrayList<>();
public PointF sourceF = null;
public PointF collisionF = null;
public static final int STOP_TARGET = 1; //ballistica will stop at the target cell
public static final int STOP_CHARS = 2; //ballistica will stop on first char hit
public static final int STOP_SOLID = 4; //ballistica will stop on solid terrain
public static final int IGNORE_SOFT_SOLID = 8; //ballistica will ignore soft solid terrain, such as doors and webs
public static final int PROJECTILE = STOP_TARGET | STOP_CHARS | STOP_SOLID;
public static final int MAGIC_BOLT = STOP_CHARS | STOP_SOLID;
public static final int WONT_STOP = 0;
public BallisticaReal(PointF from, PointF to, int params) {
//sourcepos need to offset a bit to judge cases the from is on the line
//for example, from = "30, 20.5", to = "20, 0.5", x>=30 is wall
//if we do not offset, the source will be 30, 20, and when it collides with y=20,
//the wall judgement is centered at (30, 20), not (29, 20). First judgement will look at (30, 19) instead of (29,19)
//When it collides, it regards the upper ceil, wall, not empty. And the ballistica will end here.
//if the point is on the edge of cell, give it a tiny offset.
if(from.x-(int)from.x==0f || from.y-(int)from.y==0f) {
sourceI = pointToCell(pointFloatToInt(from.clone().offset(to.x - from.x > 0 ? 0.001f : -0.001f, to.y - from.y > 0 ? 0.001f : -0.001f), false));
}else{
sourceI = pointToCell(pointFloatToInt(from.clone(), false));
}
sourceF = from;
buildTrace(sourceF, to,
(params & STOP_TARGET) > 0,
(params & STOP_CHARS) > 0,
(params & STOP_SOLID) > 0,
(params & IGNORE_SOFT_SOLID) > 0);
if (collisionI != null) {
dist = pathI.indexOf(collisionI);
} else if (!pathI.isEmpty()) {
collisionI = pathI.get(dist = pathI.size() - 1);
collisionF = pathF.get(pathF.size()-1);
} else {
dist = 0;
}
}
public BallisticaReal(int from, int to, int params){
this(pointIntToFloat(cellToPoint(from)).offset(0.5f, 0.45f),
pointIntToFloat(cellToPoint(to)).offset(0.5f, 0.45f),
params);
}
public BallisticaReal(PointF from, float angle, float range, int params){
this(from, new PointF(from.x, from.y).offset(poleToPointF(angle, range)), params);
}
public BallisticaReal(int from, float angle, float range, int params){
this(pointIntToFloat(cellToPoint(from)).offset(0.5f, 0.45f),angle,range,params);
}
private void buildTrace(PointF from, PointF to, boolean stopTarget, boolean stopChars, boolean stopTerrain, boolean ignoreSoftSolid){
PointF vector = new PointF(to.x - from.x, to.y - from.y);
float dx;
float dy;
float movX = Math.abs(vector.x);
float movY = Math.abs(vector.y);
//too short move ,return
if(movX < 0.001f && movY < 0.001f){
int end = pointToCell(pointFloatToInt(from, false));
pathI.add(end);
pathF.add(from);
cld(end);
cldF(from);
return;
}
//actually it is abandoned
if(movX>movY){
dx = (vector.x>0?1f:-1f);
dy = movY/movX*(vector.y>0?1f:-1f);
}else{
dy = (vector.y>0?1f:-1f);
dx = movX/movY*(vector.x>0?1f:-1f);
}
//which direction?
boolean up = dy>0;
boolean right = dx>0;
boolean vertical = Math.abs(dx)<0.001f;
boolean horizontal = Math.abs(dy)<0.001f;
//record current point
PointF curPosF = new PointF(from.x, from.y);
Point curPos = cellToPoint(sourceI);//pointFloatToInt(from, false);
//we shot into one tile, and meet one border. passable caches whether beam can pass this border.
boolean[] canPass = new boolean[4];
final int[] neigh = new int[]{-1, 1, Dungeon.level.width(), -Dungeon.level.width()};
pathI.add(sourceI);
pathF.add(sourceF);
/*
if (stopTerrain && Dungeon.level.solid[sourceI]) {
if (ignoreSoftSolid && (Dungeon.level.passable[sourceI] || Dungeon.level.avoid[sourceI])) {
//do nothing
} else {
cld(sourceI);
cldF(sourceF);
return;
}
} else if (stopChars && Actor.findChar( sourceI ) != null) {
cld(sourceI);
cldF(sourceF);
return;
} else if (sourceF.equals(pointFloatToInt(to, false)) && stopTarget){
cld(sourceI);
cldF(sourceF);
return;
}
*/
while(isInsideMap(curPosF)){
int cell= pointToCell(curPos);
boolean passable;
//build passable
for(int i=0;i<4;++i){
int target = cell + neigh[i];
passable = true;
if (stopTerrain && Dungeon.level.solid[target]) {
if (ignoreSoftSolid && (Dungeon.level.passable[target] || Dungeon.level.avoid[target])) {
//do nothing
} else {
passable = false;
}
} else if (stopChars && Actor.findChar( target ) != null) {
passable = false;
} else if (curPos.equals(pointFloatToInt(to, false)) && stopTarget){
passable = false;
}
canPass[i]=passable;
}
//will meet which border
boolean xOnLine = curPosF.x-(int)curPosF.x==0;
boolean yOnLine = curPosF.y-(int)curPosF.y==0;
float tx = (float) (xOnLine?(Math.floor(curPosF.x) + (right?1f:-1f)):(Math.floor(curPosF.x) + (right?1f:0f)));
float ty = (float) (yOnLine?(Math.floor(curPosF.y) + (up?1f:-1f)):(Math.floor(curPosF.y) + (up?1f:0f)));
//meet x border, y border, or both? And where?
PointF nx, ny;
if(vertical){
ny = new PointF(curPosF.x, ty);
nx = null;
}else if(horizontal){
nx = new PointF(tx, curPosF.y);
ny = null;
}else{
nx = new PointF(tx, (tx-curPosF.x)*dy/dx+curPosF.y);
ny = new PointF((ty-curPosF.y)*dx/dy+curPosF.x, ty);
if(nx.y==ny.y){
//nx==ny means reaching xy cross
//do nothing
}else if((up && nx.y>ny.y) || (!up && nx.y<ny.y)){
nx = null;
}else{
ny = null;
}
}
//out of range, stop
if(stopTarget){
if((nx != null && Math.abs(nx.x - from.x)>=movX) ||
(ny != null && Math.abs(ny.y - from.y)>=movY))
{
cldF(to);
cld(pointToCell(pointFloatToInt(to, false)));
pathI.add(collisionI);
pathF.add(collisionF);
return;
}
}
//next cell, next pointF. Handle xy logic first.
int nextCell=cell;
if(ny==null){
nextCell += dx>0? 1:-1;
}else if(nx==null){
nextCell += dy>0?Dungeon.level.width():-Dungeon.level.width();
}else{
//if reach the xy cross, judge x and y
nextCell += dx>0? 1:-1;
for(int i=0; i<4; ++i){
if(neigh[i]+cell!=nextCell) continue;
if(!canPass[i]){
cld(nextCell+dy>0?Dungeon.level.width():-Dungeon.level.width());
cldF(nx);
return;
}
}
nextCell += dy>0?Dungeon.level.width():-Dungeon.level.width();
for(int i=0; i<4; ++i){
if(neigh[i]+cell!=nextCell) continue;
if(!canPass[i]){
cld(nextCell);
cldF(ny);
return;
}
}
//if x and y let it pass, the go on
}
//judge whether can pass, and collide. For x or y logic
pathI.add(nextCell);
pathF.add(ny==null?nx.clone():ny.clone());
//has judged xy cross
if(nx == null || ny == null) {
for (int i = 0; i < 4; ++i) {
if (neigh[i] + cell != nextCell) continue;
if (!canPass[i]) {
cld(nextCell);
cldF(ny == null ? nx : ny);
return;
}
}
}
//go on one point, and continue
curPosF=(ny==null?nx.clone():ny.clone());
curPos=cellToPoint(nextCell);
}
}
private void cld(int cell){
if (collisionI == null)
collisionI = cell;
}
//using with new pointF
private void cldF(PointF cell){
if (collisionF == null) {
collisionF = cell.clone();
}
}
public List<Integer> subPath(int start, int end){
try {
end = Math.min( end, pathI.size()-1);
return pathI.subList(start, end+1);
} catch (Exception e){
ShatteredPixelDungeon.reportException(e);
return new ArrayList<>();
}
}
public static PointF pointToScreen(PointF p){
return p.clone().scale(DungeonTilemap.SIZE);
}
public static PointF raisedPointToScreen(PointF p){
return p.clone().scale(DungeonTilemap.SIZE);
}
private static final float A2P = 0.0174533f;
//We assume up is positive y but in game it is opposite.
private static PointF poleToPointF(float angle, float range){
return new PointF((float)(range*Math.cos(angle*A2P)), (float)(range*Math.sin(angle*A2P)));
}
private static boolean isInsideMap(PointF posF){
return !(posF.x<1f || posF.x> Dungeon.level.width()-1f || posF.y<1f || posF.y>Dungeon.level.height()-1f);
}
private static Point cellToPoint(int cell ){
return new Point(cell % Dungeon.level.width(), cell / Dungeon.level.width());
}
private static int pointToCell( Point p ){
return p.x + p.y*Dungeon.level.width();
}
private static Point pointFloatToInt(PointF p, boolean round){
if(round){
return new Point(Math.round(p.x), Math.round(p.y));
}
return new Point((int)p.x, (int)p.y);
}
private static PointF pointIntToFloat(Point p){
return new PointF(p.x, p.y);
}
public static PointF coordToScreen(PointF p){
return p.clone().offset(0.5f, 0.5f).scale(DungeonTilemap.SIZE);
}
}

View File

@ -337,4 +337,7 @@ public class RenderedTextBlock extends Component {
} }
} }
} }
protected void hardlight(float r, float g, float b) {
}
} }

View File

@ -21,11 +21,15 @@
package com.shatteredpixel.shatteredpixeldungeon.windows; package com.shatteredpixel.shatteredpixeldungeon.windows;
import static com.shatteredpixel.shatteredpixeldungeon.SPDSettings.HelpSettings;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hunger;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.custom.ch.GameTracker;
import com.shatteredpixel.shatteredpixeldungeon.custom.messages.M; import com.shatteredpixel.shatteredpixeldungeon.custom.messages.M;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
@ -36,6 +40,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIcon;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton; import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane; import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane;
import com.shatteredpixel.shatteredpixeldungeon.ui.StatusPane; import com.shatteredpixel.shatteredpixeldungeon.ui.StatusPane;
@ -50,6 +55,7 @@ import com.watabou.noosa.ui.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Pattern;
public class WndHero extends WndTabbed { public class WndHero extends WndTabbed {
@ -126,7 +132,7 @@ public class WndHero extends WndTabbed {
private class StatsTab extends Group { private class StatsTab extends Group {
private static final int GAP = 6; private static final int GAP = 4;
private float pos; private float pos;
@ -145,13 +151,10 @@ public class WndHero extends WndTabbed {
IconTitle title = new IconTitle(); IconTitle title = new IconTitle();
title.icon( HeroSprite.avatar(hero.heroClass, hero.tier()) ); title.icon( HeroSprite.avatar(hero.heroClass, hero.tier()) );
if (hero.name().equals(hero.className())) { if (hero.name().equals(hero.className()))
title.label(Messages.get(this, "title", Integer.valueOf(hero.lvl), hero.className()).toUpperCase(Locale.ENGLISH)); title.label( Messages.get(this, "title", hero.lvl, hero.className() ).toUpperCase( Locale.ENGLISH ) );
} else { else
title.label((hero.name() + "\n" + Messages.get(this, "title", Integer.valueOf(hero.lvl), title.label((hero.name() + "\n" + Messages.get(this, "title", hero.lvl, hero.className())).toUpperCase(Locale.ENGLISH));
hero.className())).toUpperCase(Locale.ENGLISH));
}
title.color(Window.TITLE_COLOR); title.color(Window.TITLE_COLOR);
title.setRect( 0, 0, WIDTH-16, 0 ); title.setRect( 0, 0, WIDTH-16, 0 );
add(title); add(title);
@ -176,7 +179,7 @@ public class WndHero extends WndTabbed {
infoButton.setRect(title.right(), 0, 16, 16); infoButton.setRect(title.right(), 0, 16, 16);
add(infoButton); add(infoButton);
pos = title.bottom() + 2*GAP; pos = title.bottom() + GAP;
int strBonus = hero.STR() - hero.STR; int strBonus = hero.STR() - hero.STR;
if (strBonus > 0) statSlot( Messages.get(this, "str"), hero.STR + " + " + strBonus ); if (strBonus > 0) statSlot( Messages.get(this, "str"), hero.STR + " + " + strBonus );
@ -190,20 +193,45 @@ public class WndHero extends WndTabbed {
statSlot( Messages.get(this, "gold"), Statistics.goldCollected ); statSlot( Messages.get(this, "gold"), Statistics.goldCollected );
statSlot( Messages.get(this, "depth"), Statistics.deepestFloor ); statSlot( Messages.get(this, "depth"), Statistics.deepestFloor );
statSlot( Messages.get(HeroStat.class, "seed_dungeon"), DungeonSeed.convertToCode(Dungeon.seed) );
statSlot( M.L(HeroStat.class,"seed_dungeon"), DungeonSeed.convertToCode(Dungeon.seed).toUpperCase());
pos += GAP; pos += GAP;
Hunger hunger = Dungeon.hero.buff(Hunger.class);
String hunger_str = "null";
if(hunger != null){
hunger_str = hunger.hunger() + "/" + Hunger.STARVING;
}
statSlot( M.L(HeroStat.class, "hunger"), hunger_str);
pos += GAP;
RedButton buttonItem = new RedButton(M.L(HeroStat.class, "item_enter"), 8){
@Override
protected void onClick() {
super.onClick();
GameScene.show(new StatsTab.WndTreasureGenerated());
}
};
add(buttonItem);
buttonItem.setRect(2, pos, WIDTH - 4, 16);
if(HelpSettings()){
buttonItem.active = true;
} else {
buttonItem.active = false;
buttonItem.visible = false;
}
} }
private void statSlot( String label, String value ) { private void statSlot( String label, String value ) {
RenderedTextBlock txt = PixelScene.renderTextBlock( label, 8 ); RenderedTextBlock txt = PixelScene.renderTextBlock( label, 7 );
txt.setPos(0, pos); txt.setPos(0, pos);
add( txt ); add( txt );
txt = PixelScene.renderTextBlock( value, 8 ); txt = PixelScene.renderTextBlock( value, 7 );
txt.setPos(WIDTH * 0.6f, pos); txt.setPos(WIDTH * 0.5f, pos);
PixelScene.align(txt); PixelScene.align(txt);
add( txt ); add( txt );
@ -217,6 +245,67 @@ public class WndHero extends WndTabbed {
public float height() { public float height() {
return pos; return pos;
} }
private class WndTreasureGenerated extends Window{
private static final int WIDTH = 120;
private static final int HEIGHT = 144;
public WndTreasureGenerated(){
super();
resize(WIDTH, HEIGHT);
ScrollPane pane = new ScrollPane(new Component());
Component content = pane.content();
this.add(pane);
pane.setRect(0,0,WIDTH, HEIGHT);
GameTracker gmt = Dungeon.hero.buff(GameTracker.class);
if(gmt != null){
String allInfo = gmt.itemInfo();
String[] result = allInfo.split("\n");
float pos = 2;
for(String info: result){
if(info.contains("dungeon_depth")){
pos += 4;
RenderedTextBlock depthText = PixelScene.renderTextBlock(info.replace("dungeon_depth: ", M.L(HeroStat.class, "item_wnd_depth")), 8);
depthText.maxWidth(WIDTH);
depthText.hardlight(0xFFFF00);
content.add(depthText);
depthText.setPos(0, pos);
pos += 8;
}else{
pos += 1;
info = info.replace("MIMIC_HOLD", M.L(HeroStat.class, "item_wnd_mimic"));
info = info.replace("QUEST_REWARD", M.L(HeroStat.class, "item_wnd_reward"));
info = info.replace("CURSED", M.L(HeroStat.class, "item_wnd_cursed"));
RenderedTextBlock itemText = PixelScene.renderTextBlock(info, 6);
itemText.maxWidth(WIDTH);
content.add(itemText);
itemText.setPos(0, pos);
pos += 6;
String level = Pattern.compile("[^0-9]").matcher(info).replaceAll("").trim();
try{
int lvl = Integer.parseInt(level);
if(lvl == 1){
itemText.hardlight(0x57FAFF);
}else if(lvl == 2){
itemText.hardlight(0xA000A0);
}else if(lvl == 3){
itemText.hardlight(0xFFB700);
}else if(lvl >= 4) {
itemText.hardlight(Window.Pink_COLOR);
}
}catch (Exception e){
}
}
}
content.setSize(WIDTH, pos + 2);
}
pane.scrollTo(0, 0);
}
}
} }
public class TalentsTab extends Component { public class TalentsTab extends Component {

View File

@ -32,6 +32,7 @@ import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.services.news.News; import com.shatteredpixel.shatteredpixeldungeon.services.news.News;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CrossDiedSprites;
import com.shatteredpixel.shatteredpixeldungeon.ui.CheckBox; import com.shatteredpixel.shatteredpixeldungeon.ui.CheckBox;
import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog; import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
@ -66,6 +67,7 @@ public class WndSettings extends WndTabbed {
private AudioTab audio; private AudioTab audio;
private LangsTab langs; private LangsTab langs;
private ExtendTab extabs; private ExtendTab extabs;
private HelpTab helps;
public static int last_index = 0; public static int last_index = 0;
@ -176,6 +178,20 @@ public class WndSettings extends WndTabbed {
} }
}); });
helps = new HelpTab();
helps.setSize(width, 0);
height = Math.max(height, audio.height());
add( helps );
add( new IconTab(new CrossDiedSprites()){
@Override
protected void select(boolean value) {
super.select(value);
helps.visible = helps.active = value;
if (value) last_index = 6;
}
});
resize(width, (int)Math.ceil(height)); resize(width, (int)Math.ceil(height));
layoutTabs(); layoutTabs();
@ -663,6 +679,54 @@ public class WndSettings extends WndTabbed {
} }
private static class HelpTab extends Component {
RenderedTextBlock title;
ColorBlock sep1;
CheckBox LockFing;
@Override
protected void createChildren() {
title = PixelScene.renderTextBlock(Messages.get(this, "title"), 9);
title.hardlight(TITLE_COLOR);
add(title);
sep1 = new ColorBlock(1, 1, 0xFF000000);
add(sep1);
LockFing = new CheckBox( Messages.get(this, "helpsettings") ) {
@Override
protected void onClick() {
super.onClick();
SPDSettings.HelpSettings(checked());
}
};
LockFing.checked(SPDSettings.HelpSettings());
add(LockFing);
}
@Override
protected void layout() {
float bottom = y;
title.setPos((width - title.width())/2, bottom + GAP);
sep1.size(width, 1);
sep1.y = title.bottom() + 2*GAP;
bottom = sep1.y + 1;
if (width > 200){
LockFing.setRect(0, bottom, width, SLIDER_HEIGHT);
} else {
LockFing.setRect(0, bottom + GAP, width, SLIDER_HEIGHT);
}
height = LockFing.bottom();
}
}
private static class DataTab extends Component{ private static class DataTab extends Component{
RenderedTextBlock title; RenderedTextBlock title;