diff --git a/battle/attack/AtkModelEnemy.java b/battle/attack/AtkModelEnemy.java index 562b0a0e..413aed66 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", "LETHARGY", "DMGCUT", "DMGCAP", "DELAY" }; + "POIATK", "ARMOR", "SPEED", "LETHARGY", "DMGCUT", "DMGCAP", "DELAY", "CHANGEMONEY" }; 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 b816fec0..dd99a35d 100755 --- a/battle/attack/AtkModelEntity.java +++ b/battle/attack/AtkModelEntity.java @@ -225,6 +225,11 @@ public void invokeLater(AttackAb atk, Entity e) { summon(proc, e, e, rst); } } + if (atk.getProc().CHANGEMONEY.exists()) { + if (e.health <= 0) { + atk.attacker.triggerChangeMoney(2); // on kill + } + } } /** @@ -278,7 +283,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", "LETHARGY", "MINIWAVE", "SHIELDBREAK", - "MINIVOLC", "METALKILL", "BLAST", "DELAY" + "MINIVOLC", "METALKILL", "BLAST", "DELAY", "CHANGEMONEY" }; for (String s0 : par) diff --git a/battle/attack/ContVolcano.java b/battle/attack/ContVolcano.java index 3004e6b3..01726915 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", "LETHARGY", "DMGCUT", "DMGCAP", "DELAY"}; + String[] cursep = {"KB", "STOP", "SLOW", "WEAK", "WARP", "CURSE", "SNIPER", "SEAL", "POISON", "BOSS", "POIATK", "ARMOR", "SPEED", "LETHARGY", "DMGCUT", "DMGCAP", "DELAY", "CHANGEMONEY"}; 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/ECastle.java b/battle/entity/ECastle.java index 6413e0cf..989a184f 100755 --- a/battle/entity/ECastle.java +++ b/battle/entity/ECastle.java @@ -61,6 +61,7 @@ public boolean damaged(AttackAb atk) { int ans = atk.atk; ans = (int) (ans * (1 + atk.getProc().ATKBASE.mult / 100.0)); + atk.attacker.triggerChangeMoney(4); // on hit base int satk = atk.getProc().SATK.mult; if (satk > 0) { diff --git a/battle/entity/Entity.java b/battle/entity/Entity.java index eb800b9a..29b02087 100755 --- a/battle/entity/Entity.java +++ b/battle/entity/Entity.java @@ -24,6 +24,7 @@ import common.util.stage.StageLimit; import common.util.unit.Level; import common.util.unit.Trait; +import org.jcodec.common.tools.MathUtil; import java.util.*; @@ -1478,6 +1479,19 @@ public void update() { */ private int regentimer; + /** + * change money cooldown + */ + private int bountycooldown; + /** + * change money cooldown that it wants to be set to (for multi hits) + */ + private int trybountycooldown; + /** + * change money tracking of multi hits + */ + private boolean firstbountydone; + public final Proc proc; protected Entity(StageBasis b, MaskEnemy de, EAnimU ea, float atkMagnif, float hpMagnif) { @@ -1565,6 +1579,11 @@ public boolean damaged(AttackAb atk) { regentimer = getProc().HPREGEN.interval; // Reset if enabled } + atk.attacker.triggerChangeMoney(1); // on hit + if (isBase) { + atk.attacker.triggerChangeMoney(4); // on hit base + } + if (anim.corpse != null && anim.corpse.type == ZombieEff.REVIVE && status[P_REVIVE][1] >= REVIVE_SHOW_TIME) return false; @@ -1865,6 +1884,81 @@ else if (basis.activeGuard != 1) return true; } + public void triggerChangeMoney(int context) { + boolean isFirstTrigger = false; + if (getProc().CHANGEMONEY.prob <= 0) { + return; + } + if (getProc().CHANGEMONEY.condition != context) { + return; + } + if (getProc().CHANGEMONEY.condition != 0 && health <= 0) { + return; + } + if (firstbountydone && !getProc().CHANGEMONEY.hitstacks) { + return; + } + if (bountycooldown > 0) { + return; + } + if (getProc().CHANGEMONEY.freezeeff && status[P_STOP][0] > 0) { + return; + } + if (!firstbountydone) { + isFirstTrigger = true; + } + firstbountydone = true; + float actualChance = getProc().CHANGEMONEY.prob / 100f; + if (basis.r.nextFloat() < actualChance) { + if (getProc().CHANGEMONEY.usesound && isFirstTrigger) { + switch (getProc().CHANGEMONEY.sound) { + case 0: + CommonStatic.setSE(14); + break; + case 1: + CommonStatic.setSE(13); + break; + case 2: + CommonStatic.setSE(12); + break; + case 3: + CommonStatic.setSE(190); + break; + case 4: + CommonStatic.setSE(107); + break; + case 5: + CommonStatic.setSE(64); + break; + } + } + float actualPercent = (getProc().CHANGEMONEY.amount/100f); + switch (getProc().CHANGEMONEY.type) { + case 0: // change by flat + basis.money += getProc().CHANGEMONEY.amount * 100; + break; + case 1: // change by % of max + basis.money += (int)(basis.maxMoney * actualPercent); + break; + case 2: // change by % of current + basis.money = (int)(basis.money * (1 + actualPercent)); + break; + case 3: // change by % of missing + int missing = basis.maxMoney - basis.money; + basis.money += (int)(missing * actualPercent); + break; + case 4: // set to amount + basis.money = getProc().CHANGEMONEY.amount * 100; + break; + case 5: // set to % of max + basis.money = (int)(basis.maxMoney * actualPercent); + break; + } + basis.money = MathUtil.clip(basis.money,0,basis.maxMoney); + } + trybountycooldown = getProc().CHANGEMONEY.cooldown; + } + private int applyLethargy(int tba) { if (status[P_LETHARGY][0] > 0) { if (status[P_LETHARGY][2] == 0) { @@ -2207,6 +2301,18 @@ public void cont() { @Override public void postUpdate() { regenUpdate(); + if ((getProc().CHANGEMONEY.freezeeff && status[P_STOP][0] == 0) || !getProc().CHANGEMONEY.freezeeff) { + bountycooldown = Math.max(bountycooldown-1,-1); + } + firstbountydone = false; + triggerChangeMoney(3); // constantly + if (getProc().CHANGEMONEY.condition == 5 && !walking && !dead) { + triggerChangeMoney(5); // while idle/attacking + } + if (trybountycooldown != -1) { + bountycooldown = trybountycooldown; + trybountycooldown = -1; + } int hb = data.getHb(); long ext = health * hb % maxH; @@ -2258,6 +2364,10 @@ public void postUpdate() { tokens.get(i).model.invokeLater(tokens.get(i), this); tokens.clear(); + if (health <= 0) { + triggerChangeMoney(0); // on death + } + if(isBase && health <= 0) kbTime = 1; diff --git a/util/Data.java b/util/Data.java index e7728bf8..d0fb4f6a 100755 --- a/util/Data.java +++ b/util/Data.java @@ -245,6 +245,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) @@ -779,6 +781,46 @@ public static class DELAY extends ProcItem { public int type; } + @JsonClass(noTag = NoTag.LOAD) + public static class CHANGEMONEY extends ProcItem { + @Order(0) + public int prob; + @Order(1) + public int condition; + @Order(2) + public int amount; + @Order(3) + public int type; + @Order(4) + public int cooldown; + @Order(5) + public int sound; + @Order(6) + public boolean usesound; + @Order(7) + public boolean freezeeff; + } + + @JsonClass(noTag = NoTag.LOAD) + public static class CHANGEMONEYPROC extends ProcItem { + @Order(0) + public int prob; + @Order(1) + public int condition; + @Order(2) + public int amount; + @Order(3) + public int type; + @Order(4) + public int sound; + @Order(5) + public boolean usesound; + @Order(6) + public boolean hitstacks; + @Order(7) + public boolean freezeeff; + } + public static Proc blank() { return new Proc(); } @@ -942,6 +984,8 @@ public static Proc load(int[][] data) { public final DELAY DELAY = new DELAY(); @Order(65) public final IMUAD IMUDELAY = new IMUAD(); + @Order(66) + public final CHANGEMONEY CHANGEMONEY = new CHANGEMONEY(); // Talent orbs, shouldn't be given @Order unless allowed as editable fields public final MINIVOLC MINIDEATHSURGE = new MINIVOLC(); // TODO: implement this as a normal ability? @@ -1332,7 +1376,9 @@ public static Proc genProc(JsonElement elem) { public static final int P_IMULETH = 63; public static final int P_DELAY = 64; // Works like Speed public static final int P_IMUDELAY = 65; - public static final byte PROC_TOT = 66; + public static final int P_CHANGEMONEY = 66; + public static final int P_CHANGEMONEYPROC = 67; + public static final byte PROC_TOT = 68; public static final byte PROC_WIDTH = 6; public static final int SCORE_WEAK = 0; @@ -1412,7 +1458,8 @@ public static Proc genProc(JsonElement elem) { false, //lethargy true, //imu.lethargy false, //delay - true //imu delay + true, //imu delay + true //bounty }; /** diff --git a/util/Res.java b/util/Res.java index d704f2f3..ab727810 100755 --- a/util/Res.java +++ b/util/Res.java @@ -292,6 +292,8 @@ private static void readAbiIcon() { 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_LETHARGY] = new VImg("./org/page/icons/Lethargy.png"); + aux.icon[1][P_CHANGEMONEY] = new VImg("./org/page/icons/Bounty.png"); + aux.icon[1][P_CHANGEMONEYPROC] = new VImg("./org/page/icons/Bounty.png"); CommonStatic.getConfig().icon = false; } diff --git a/util/lang/Editors.java b/util/lang/Editors.java index 9358ed44..ee3c2500 100644 --- a/util/lang/Editors.java +++ b/util/lang/Editors.java @@ -783,6 +783,24 @@ else if (t.killCount == 0) })); map().put("IMUDELAY", imuad); + + map().put("CHANGEMONEY", new EditControl<>(Proc.CHANGEMONEY.class, (t) -> { + t.prob = MathUtil.clip(t.prob, 0, 100); + if (t.prob == 0) { + t.type = 0; + t.condition = 0; + t.amount = 0; + t.hitstacks = false; + t.usesound = false; + t.sound = 0; + t.cooldown = 0; + } else { + t.condition = MathUtil.clip(t.condition, 0, 5); + t.sound = MathUtil.clip(t.sound, 0, 5); + t.cooldown = Math.max(0, t.cooldown); + t.type = MathUtil.clip(t.type, 0, 5); + } + })); } private static void setComponentVisibility(EditorGroup egg, boolean boo, int... fields) { diff --git a/util/lang/assets/proc.json b/util/lang/assets/proc.json index bfe1116c..60cf8d00 100644 --- a/util/lang/assets/proc.json +++ b/util/lang/assets/proc.json @@ -1597,5 +1597,107 @@ "tooltip": "0 = Resist both Delay and Advance. 1 = Only resistant to Delay. -1 = Only resistant to Advance" } } + }, + "CHANGEMONEY": { + "abbr_name": "bounty", + "full_name": "Bounty", + "tooltip": "Change money by an amount when some condition happens.", + "format": [ + "[(amount<0){Tax}(amount>0){Bounty}]", + " (_left)", + "[(condition==0){On death, }][(condition==1){Constantly, }][(condition==2){While idle, }]", + "[(type==0){change money by (amount), }][(type==1){change money by (amount)% of max, }][(type==2){change money by (amount)% of current money, }][(type==3){change money by (amount)% of missing money, }][(type==4){set money to (amount), }][(type==5){set money to (amount)% of max, }]", + "[(cooldown>0){with a cooldown of (_dispTime(cooldown)), }]", + "[(freezeeff){affected by freeze, }]", + "[(usesound){playing [(sound==0){upgrade sound}(sound==1){unused #13 sound}(sound==2){unused #12 sound}(sound==3){destiny rank sound}(sound==4){meow medal sound}(sound==5){dojo rank sound}] on trigger}][(!usesound){no sound}]", + "(_right)" + ], + "class": { + "prob": { + "name": "chance", + "tooltip": null + }, + "condition": { + "name": "condition", + "tooltip": "0: on death, 1: on hit enemy, 2: on kill enemy, 3: constantly, 4: on hit base, 5: while idle/attacking" + }, + "cooldown": { + "name": "cooldown", + "tooltip": "Amount of time that must pass before the ability can activate again" + }, + "amount": { + "name": "amount", + "tooltip": "Amount to change money by" + }, + "type": { + "name": "type", + "tooltip": "0: change by flat amount, 1: change by % of max wallet, 2: change by % of current money, 3: change by % of missing money, 4: set to money, 5: set to % of max money" + }, + "sound": { + "name": "sound", + "tooltip": "0: upgrade sound, 1: unused sound 13, 2: unused sound 12, 3: destiny rank sfx, 4: meow medal sfx, 5: dojo rank sfx" + }, + "usesound": { + "name": "use sound", + "tooltip": "Enable to play a sound when this ability triggers." + }, + "freezeeff": { + "name": "affected by freeze", + "tooltip": "Ability and constant effects are disabled while frozen." + } + } + }, + "CHANGEMONEYPROC": { + "abbr_name": "atk. bounty", + "full_name": "Atk. Bounty", + "tooltip": "Change money by an amount when some condition happens.", + "format": [ + "[(amount<0){Tax}(amount>0){Bounty}]", + " (_left)", + "[(condition==0){On hit, }][(condition==1){On kill, }]", + "[(type==0){change money by (amount), }][(type==1){change money by (amount)% of max, }][(type==2){change money by (amount)% of current money, }][(type==3){change money by (amount)% of missing money, }][(type==4){set money to (amount), }][(type==5){set money to (amount)% of max, }]", + "[(hitstacks){stacking with multiple hits, }]", + "[(freezeeff){affected by freeze, }]", + "[(usesound){playing [(sound==0){upgrade sound}(sound==1){unused #13 sound}(sound==2){unused #12 sound}(sound==3){destiny rank sound}(sound==4){meow medal sound}(sound==5){dojo rank sound}] on trigger}][(!usesound){no sound}]", + "(_right)" + ], + "class": { + "prob": { + "name": "chance", + "tooltip": null + }, + "condition": { + "name": "condition", + "tooltip": "0: on death, 1: on hit enemy, 2: on kill enemy, 3: constantly, 4: on hit base, 5: while idle/attacking" + }, + "cooldown": { + "name": "cooldown", + "tooltip": "Amount of time that must pass before the ability can activate again" + }, + "amount": { + "name": "amount", + "tooltip": "Amount to change money by" + }, + "type": { + "name": "type", + "tooltip": "0: change by flat amount, 1: change by % of max wallet, 2: change by % of current money, 3: change by % of missing money, 4: set to money, 5: set to % of max money" + }, + "sound": { + "name": "sound", + "tooltip": "0: upgrade sound, 1: unused sound 13, 2: unused sound 12, 3: destiny rank sfx, 4: meow medal sfx, 5: dojo rank sfx" + }, + "usesound": { + "name": "use sound", + "tooltip": "Enable to play a sound when this ability triggers." + }, + "hitstacks": { + "name": "stack multiple", + "tooltip": "When hitting multiple enemies at once, the ability triggers multiple times if this is enabled." + }, + "freezeeff": { + "name": "affected by freeze", + "tooltip": "Ability and constant effects are disabled while frozen." + } + } } } \ No newline at end of file