diff --git a/battle/attack/AtkModelEnemy.java b/battle/attack/AtkModelEnemy.java index 46de6fe1..d81bd4b9 100755 --- a/battle/attack/AtkModelEnemy.java +++ b/battle/attack/AtkModelEnemy.java @@ -19,7 +19,7 @@ public class AtkModelEnemy extends AtkModelEntity { protected AtkModelEnemy(EEnemy ent, float d0) { super(ent, d0, 1); String[] arr = { "KB", "STOP", "SLOW", "WEAK", "WARP", "CURSE", "SNIPER", "SEAL", "POISON", "BOSS", - "POIATK", "ARMOR", "SPEED", "DMGCUT", "DMGCAP" }; + "POIATK", "ARMOR", "BONECRUSH", "SPEED", "DMGCUT", "DMGCAP" }; cursed = new Proc[data.getAtkCount()]; for (int i = 0; i < cursed.length; i++) { cursed[i] = data.getAtkModel(i).getProc().clone(); diff --git a/battle/attack/AtkModelEntity.java b/battle/attack/AtkModelEntity.java index 3dff5a32..793c70d5 100755 --- a/battle/attack/AtkModelEntity.java +++ b/battle/attack/AtkModelEntity.java @@ -280,7 +280,7 @@ protected void setProc(int ind, Proc proc) { String[] par = { "CRIT", "WAVE", "KB", "WARP", "STOP", "SLOW", "WEAK", "POISON", "MOVEWAVE", "CURSE", "SNIPER", "BOSS", "SEAL", "BREAK", "SUMMON", "SATK", "POIATK", "VOLC", "ARMOR", "SPEED", "MINIWAVE", "SHIELDBREAK", - "MINIVOLC", "METALKILL", "BLAST" + "MINIVOLC", "METALKILL", "BLAST", "BONECRUSH" }; for (String s0 : par) diff --git a/battle/attack/AttackAb.java b/battle/attack/AttackAb.java index 3e5b49d7..5027dc55 100755 --- a/battle/attack/AttackAb.java +++ b/battle/attack/AttackAb.java @@ -187,6 +187,14 @@ protected void process() { else proc.ARMOR.time *= (100 - imus.IMUARMOR.block) / 100.0; } + if (proc.BONECRUSH.time > 0 && imus.IMUBONE.block != 0 && checkAIImmunity(proc.BONECRUSH.mult, imus.IMUBONE.smartImu, imus.IMUBONE.block < 0)) { + if (imus.IMUBONE.block > 0) + blocked = true; + if (imus.IMUBONE.block == 100) + proc.BONECRUSH.clear(); + else + proc.BONECRUSH.time *= (100 - imus.IMUBONE.block) / 100.0; + } if (proc.SPEED.time > 0 && imus.IMUSPEED.block != 0) { boolean b; if (proc.SPEED.type != 2) diff --git a/battle/attack/ContVolcano.java b/battle/attack/ContVolcano.java index 7d06ddb0..4acf6849 100644 --- a/battle/attack/ContVolcano.java +++ b/battle/attack/ContVolcano.java @@ -153,7 +153,7 @@ private void updateProc() { } performed[1] = performed[3] = true; } - String[] cursep = {"KB", "STOP", "SLOW", "WEAK", "WARP", "CURSE", "SNIPER", "SEAL", "POISON", "BOSS", "POIATK", "ARMOR", "SPEED", "DMGCUT", "DMGCAP"}; + String[] cursep = {"KB", "STOP", "SLOW", "WEAK", "WARP", "CURSE", "SNIPER", "SEAL", "POISON", "BOSS", "POIATK", "ARMOR", "SPEED", "DMGCUT", "DMGCAP", "BONECRUSH"}; if (v.attacker.status[P_CURSE][0] > 0 || v.attacker.status[P_SEAL][0] > 0 && performed[2]) { performed[2] = false; for (String s : cursep) diff --git a/battle/entity/Entity.java b/battle/entity/Entity.java index 34b40ef7..5f7d5dfe 100755 --- a/battle/entity/Entity.java +++ b/battle/entity/Entity.java @@ -414,6 +414,11 @@ public void getEff(int t) { EffAnim eff = effas().A_E_GUARD; effs[id] = eff.getEAnim(GuardEff.BREAK); CommonStatic.setSE(SE_BARRIER_ABI); + } else if (t == P_BONECRUSH) { + int id = A_BONECRUSH; + EffAnim eff = effas().A_BONECRUSH; + BoneEff index = status[P_BONECRUSH][1] >= 0 ? BoneEff.DEBUFF : BoneEff.BUFF; + effs[id] = eff.getEAnim(index); } } @@ -487,6 +492,11 @@ private void checkEff() { effs[id] = null; } + if (status[P_BONECRUSH][0] == 0) { + byte id = A_BONECRUSH; + effs[id] = null; + } + if (status[P_SPEED][0] == 0) { byte id = dire == -1 ? A_SPEED : A_E_SPEED; effs[id] = null; @@ -2014,6 +2024,20 @@ private void processProcs(AttackAb atk) { anim.getEff(INV); } + if (!isBase && atk.getProc().BONECRUSH.time > 0) { + int res = checkAIImmunity(atk.getProc().BONECRUSH.mult, getProc().IMUBONE.smartImu, getProc().IMUBONE.mult < 0) ? getProc().IMUBONE.mult : 0; + + if (res < 100) { + int val = (int) (atk.getProc().BONECRUSH.time * time); + status[P_BONECRUSH][0] = val * (100 - res) / 100; + status[P_BONECRUSH][1] = atk.getProc().BONECRUSH.mult; + status[P_BONECRUSH][2] = atk.getProc().BONECRUSH.single ? 1 : 0; + + anim.getEff(P_BONECRUSH); + } else + anim.getEff(INV); + } + if (atk.getProc().SPEED.time > 0) { int res = getProc().IMUSPEED.mult; int speed = data.getSpeed(); @@ -2131,8 +2155,17 @@ public void postUpdate() { if (status[P_ARMOR][0] > 0) { damage = (long) (damage * (100 + status[P_ARMOR][1]) / 100.0); } - if (!isBase && damage > 0 && kbTime <= 0 && kbTime != -1 && (ext <= damage * hb || health < damage)) - interrupt(INT_HB, KB_DIS[INT_HB]); + if (!isBase && damage > 0 && kbTime <= 0 && kbTime != -1 && (ext <= damage * hb || health < damage)) { + if (status[P_BONECRUSH][0] > 0) { + damage = (long) (damage * (100 + status[P_BONECRUSH][1]) / 100.0); + if (status[P_BONECRUSH][2] == 1) { + status[P_BONECRUSH][0] = 0; + } + } + if (damage != 0) { + interrupt(INT_HB, KB_DIS[INT_HB]); + } + } if (damage > 0 && isBase && basis.activeGuard == 1) { anim.getEff(GUARD_HOLD); } else { @@ -2633,6 +2666,8 @@ private void updateProc() { status[P_ARMOR][0]--; if (status[P_SPEED][0] > 0) status[P_SPEED][0]--; + if (status[P_BONECRUSH][0] > 0) + status[P_BONECRUSH][0]--; if (status[P_BSTHUNT][0] > 0) status[P_BSTHUNT][0]--; // update tokens diff --git a/util/Data.java b/util/Data.java index 67b1007d..1f72ae31 100755 --- a/util/Data.java +++ b/util/Data.java @@ -34,7 +34,6 @@ public static class ARMOR extends ProcItem { @Order(2) public int mult; } - @JsonClass(noTag = NoTag.LOAD) public static class BURROW extends ProcItem { @Order(0) @@ -227,6 +226,8 @@ public ProcItem clear() { for (Field f : fs) if (f.getType() == int.class) f.set(this, 0); + else if (f.getType() == boolean.class) + f.set(this, false); else if (IntType.class.isAssignableFrom(f.getType())) f.set(this, (f.getType().getDeclaredConstructor().newInstance())); else if (f.getType() == Identifier.class) @@ -475,6 +476,18 @@ public static class SPEED extends ProcItem { public int type; } + @JsonClass(noTag = NoTag.LOAD) + public static class BONECRUSH extends ProcItem { + @Order(0) + public int prob; + @Order(1) + public int time; + @Order(2) + public int mult; + @Order(3) + public boolean single; + } + @JsonClass(noTag = NoTag.LOAD) public static class STRONG extends ProcItem { @Order(0) @@ -883,13 +896,17 @@ public static Proc load(int[][] data) { public final BLAST BLAST = new BLAST(); @Order(61) public final IMU IMUBLAST = new IMU(); - @Order(61) - public final MINIVOLC MINIDEATHSURGE = new MINIVOLC(); // TODO: implement this as a normal ability? @Order(62) - public final MULT MONEYBACK = new MULT(); + public final BONECRUSH BONECRUSH = new BONECRUSH(); @Order(63) - public final MULT CANONCHARGE = new MULT(); + public final IMUAD IMUBONE = new IMUAD(); @Order(64) + public final MINIVOLC MINIDEATHSURGE = new MINIVOLC(); // TODO: implement this as a normal ability? + @Order(65) + public final MULT MONEYBACK = new MULT(); + @Order(66) + public final MULT CANONCHARGE = new MULT(); + @Order(67) public final PROB IMUATKANY = new PROB(); @Override @@ -1265,7 +1282,9 @@ public static Proc genProc(JsonElement elem) { public static final int P_HPREGEN = 59; public static final int P_BLAST = 60; public static final int P_IMUBLAST = 61; - public static final byte PROC_TOT = 62; + public static final int P_BONECRUSH = 62; + public static final int P_IMUBONE = 63; + public static final byte PROC_TOT = 64; public static final byte PROC_WIDTH = 6; public static final boolean[] procSharable = { @@ -1330,7 +1349,9 @@ public static Proc genProc(JsonElement elem) { true, //adrenaline true, //hp regen false, //blast - true //imu.blast + true, //imu.blast + true, //mini death surge + false //bone crush }; /** @@ -1342,7 +1363,7 @@ public static Proc genProc(JsonElement elem) { * Procs in this list are removed when an unit is hit and has a barrier or Aku shield active */ public static final byte[] REMOVABLE_PROC = { - P_STOP, P_SLOW, P_WEAK, P_CURSE, P_SEAL, P_POISON, P_ARMOR, P_SPEED + P_STOP, P_SLOW, P_WEAK, P_CURSE, P_SEAL, P_POISON, P_ARMOR, P_SPEED, P_BONECRUSH }; public static final byte WT_WAVE = 1; @@ -1553,8 +1574,9 @@ public static Proc genProc(JsonElement elem) { public static final byte A_DMGCAP = 64; public static final byte A_E_DMGCAP = 65; public static final byte A_E_GREEN_GUARD = 66; + public static final byte A_BONECRUSH = 67; public static final byte[] A_POIS = { A_POI0, A_POI1, A_POI2, A_POI3, A_POI4, A_POI5, A_POI6, A_POI7 }; - public static final byte A_TOT = 67; + public static final byte A_TOT = 68; // atk type index used in filter page public static final byte ATK_SINGLE = 0; diff --git a/util/Res.java b/util/Res.java index da8a4e84..99eda1de 100755 --- a/util/Res.java +++ b/util/Res.java @@ -281,6 +281,8 @@ private static void readAbiIcon() { aux.icon[1][P_SPEED] = new VImg("./org/page/icons/Speed.png"); aux.icon[1][P_SPEEDUP] = new VImg("./org/page/icons/Speed.png"); aux.icon[1][P_HPREGEN] = new VImg("./org/page/icons/Barrier.png"); + aux.icon[1][P_BONECRUSH] = new VImg("./org/page/icons/BoneCrush.png"); + aux.icon[1][P_IMUBONE] = new VImg("./org/page/icons/BoneCrushX.png"); CommonStatic.getConfig().icon = false; } diff --git a/util/lang/AnimTypeLocale.java b/util/lang/AnimTypeLocale.java index b2f967a7..dce14f25 100644 --- a/util/lang/AnimTypeLocale.java +++ b/util/lang/AnimTypeLocale.java @@ -43,6 +43,7 @@ public class AnimTypeLocale { Collections.addAll(TYPES, EffAnim.DmgCap.values()); Collections.addAll(TYPES, EffAnim.GuardEff.values()); Collections.addAll(TYPES, EffAnim.BlastEff.values()); + Collections.addAll(TYPES, EffAnim.BoneEff.values()); } public static void read() { diff --git a/util/lang/Editors.java b/util/lang/Editors.java index 2af648c7..84b1b895 100644 --- a/util/lang/Editors.java +++ b/util/lang/Editors.java @@ -671,6 +671,8 @@ else if (t.mult == 0) map().put("IMUARMOR", imuad); + map().put("IMUBONE", imuad); + map().put("IMUSPEED", imuad); map().put("BARRIER", new EditControl<>(Proc.BARRIER.class, (t) -> { @@ -733,6 +735,15 @@ else if (t.mult == 0) map().put("BLAST", new EditControl<>(Proc.BLAST.class, (t) -> { t.prob = Math.max(0, Math.min(100, t.prob)); })); + + map().put("BONECRUSH", new EditControl<>(Proc.BONECRUSH.class, (t) -> { + t.prob = MathUtil.clip(t.prob, 0, 100); + if (t.prob == 0) { + t.mult = t.time = 0; + } else { + t.time = Math.max(1, t.time); + } + })); } private static void setComponentVisibility(EditorGroup egg, boolean boo, int... fields) { diff --git a/util/lang/assets/animation_type.json b/util/lang/assets/animation_type.json index ebb76214..e63a69bf 100644 --- a/util/lang/assets/animation_type.json +++ b/util/lang/assets/animation_type.json @@ -83,5 +83,9 @@ "START": "Start", "EXPLODE": "Explode", "DUMMY": "Dummy" + }, + "BoneEff": { + "BUFF": "parry", + "DEBUFF": "bonecrush" } } \ No newline at end of file diff --git a/util/lang/assets/proc.json b/util/lang/assets/proc.json index 513c8da5..0ed6838c 100644 --- a/util/lang/assets/proc.json +++ b/util/lang/assets/proc.json @@ -1440,5 +1440,53 @@ "tooltip": "Set from 1 to 99 to reduce duration amongst all units. Set to 100 to completely block slow. Set negative to increase the effect." } } + }, + "BONECRUSH": { + "abbr_name": "bone crush", + "full_name": "Bone Crush", + "tooltip": "Changes damage from attacks that cause hitbacks", + "format": "(prob)% chance to inflict [(mult<0){parry}(mult>0){bone crush}], changing damage by (mult)% for (_dispTime(time))[(single){, only triggers once}]", + "class": { + "prob": { + "name": "chance", + "tooltip": null + }, + "time": { + "name": "duration", + "tooltip": "" + }, + "mult": { + "name": "percent", + "tooltip": "Damage multiplier, -50% means half damage from hb, 100% means double" + }, + "single": { + "name": "only trigger once", + "tooltip": "When on, the proc is removed after it triggers once" + } + } + }, + "IMUBONE": { + "abbr_name": "Imu. BoneCrush", + "full_name": "Immune/Resistant to Bone Crush", + "tooltip": null, + "format": ["[(mult!=0&block!=100){[(mult==100){Immune}(mult<100&mult>0){Resistant}(mult<0){Weak}] to Bone Crush", + "[(mult<100&mult>0){ (_left)Decreases duration by (mult)%(_right)}(mult<0){ (_left)Increases duration by (_abs(mult))%(_right)}]", + "[(block!=0&block<100){ & }]}][(block!=0){Bone Crush [(block==100){blocker}(block<100&block>0){absorber}(block<0){increaser}]", + "[(block<100&block>0){ (_left)Decreases duration by (block)%(_right)}(block<0){ (_left)Increases duration by (_abs(block))%(_right)}]}]", + "[(smartImu!=0){. Only affects [(smartImu>0){bone crush}(smartImu<0){parry}]}]"], + "class": { + "mult": { + "name": "percent", + "tooltip": "Set negative to make this unit weak to the effect" + }, + "block": { + "name": "Blocker", + "tooltip": "Set from 1 to 99 to reduce damage. Set to 100 to completely block bone crush. Set negative to increase the effect." + }, + "smartImu": { + "name": "Ignore", + "tooltip": "0 = Resist buffs and debuffs, 1 = Only resistant to debuffs, -1 = Only resistant to buffs" + } + } } } \ No newline at end of file diff --git a/util/pack/EffAnim.java b/util/pack/EffAnim.java index 30bc6776..20595527 100755 --- a/util/pack/EffAnim.java +++ b/util/pack/EffAnim.java @@ -31,6 +31,21 @@ public String path() { } } + public enum BoneEff implements EffType { + BUFF("buff"), DEBUFF("debuff"); + + private final String path; + + BoneEff(String str) { + path = str; + } + + @Override + public String path() { + return path; + } + } + public enum BarrierEff implements EffType { BREAK("_breaker"), DESTR("_destruction"), NONE(""); @@ -285,6 +300,8 @@ public static class EffAnimStore { public EffAnim A_BLAST; @Order(81) public EffAnim A_E_BLAST; + @Order(82) + public EffAnim A_BONECRUSH; public EffAnim[] values() { Field[] fld = FieldOrder.getDeclaredFields(EffAnimStore.class); @@ -654,6 +671,13 @@ private static void readCustom(String[] stfs) { effas.A_DMGCAP.rev = true; effas.A_E_DMGCAP = new EffAnim<>(dmgcap, vdmgcap, icdmgcap, DmgCap.values()); + + String root = "./org/battle/"; + String bonecrush = root + "bonecrush/bonecrush"; + VImg vbonecrush = new VImg(bonecrush+".png"); + ImgCut icbonecrush = ImgCut.newIns(bonecrush+".imgcut"); + effas.A_BONECRUSH = new EffAnim<>(bonecrush, vbonecrush, icbonecrush, BoneEff.values()); + effas.A_BONECRUSH.rev = true; } private final VImg vimg;