Files
Server/src/engine/powers/effectmodifiers/HealthEffectModifier.java
T
MagicBotandClaude Opus 5.5 dee936d8a5 Remove the DB effect loaders
Effects, modifiers, source types, effect fail conditions and item
enchant values all come from Effects.cfg (4.6b). Removes
dbEffectsBaseHandler, dbPowerHandler, the EffectsBase and effect
modifier ResultSet constructors, EffectsBase.getFailConditions and
LOAD_ENCHANT_VALUES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:17:24 -04:00

316 lines
14 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.powers.effectmodifiers;
import engine.gameManager.DispatchManager;
import engine.jobs.AbstractEffectJob;
import engine.jobs.DamageOverTimeJob;
import engine.mbEnums;
import engine.mbEnums.GameObjectType;
import engine.mbEnums.ModType;
import engine.mbEnums.SourceType;
import engine.net.AbstractNetMsg;
import engine.net.client.msg.ModifyHealthKillMsg;
import engine.net.client.msg.ModifyHealthMsg;
import engine.objects.*;
import engine.parsers.objects.wpak.EffectTemplate;
import org.pmw.tinylog.Logger;
import java.util.concurrent.ThreadLocalRandom;
public class HealthEffectModifier extends AbstractEffectModifier {
private mbEnums.DamageType damageType;
public HealthEffectModifier(String IDString, EffectTemplate.ModEntry modEntry) {
super(IDString, modEntry);
this.damageType = mbEnums.DamageType.getDamageType(this.type);
}
public static float getMinDamage(float baseMin, float intelligence, float spirit, float focus) {
float min = baseMin * (((float) Math.pow(intelligence, 0.75f) * 0.0311f) + (0.02f * (int) focus) + ((float) Math.pow(spirit, 0.75f) * 0.0416f));
return (float) ((int) (min + 0.5f)); //round to nearest whole number
}
public static float getMaxDamage(float baseMax, float intelligence, float spirit, float focus) {
float max = baseMax * (((float) Math.pow(intelligence, 0.75f) * 0.0785f) + (0.015f * (int) focus) + ((float) Math.pow(spirit, 0.75f) * 0.0157f));
return (float) ((int) (max + 0.5f)); //round to nearest whole number
}
@Override
protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) {
if (awo == null) {
Logger.error("_applyEffectModifier(): NULL AWO passed in.");
return;
}
if (effect == null) {
Logger.error("_applyEffectModifier(): NULL AbstractEffectJob passed in.");
return;
}
float modAmount = 0f;
// Modify health by percent
if (this.percentMod != 0f) {
//high level mobs/players should not be %damaged/healed.
if (awo.getHealthMax() > 25000f && (this.percentMod < 0f || this.percentMod > 5f))
return;
float mod = 1f;
if (this.useRampAdd)
mod = (this.percentMod + (this.ramp * trains)) / 100;
else
mod = (this.percentMod * (1 + (this.ramp * trains))) / 100;
modAmount = mod * awo.getHealthMax();
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
if (((AbstractCharacter) awo).isSit())
modAmount *= 2.5f;
}
}
// Modify health by min/max amount
else if (this.minMod != 0f || this.maxMod != 0f) {
float min = this.minMod;
float max = this.maxMod;
if (this.ramp > 0f) {
float mod = this.ramp * trains;
if (this.useRampAdd) {
min += mod;
max += mod;
} else {
min *= (1 + mod);
max *= (1 + mod);
}
}
if (source.getObjectType().equals(GameObjectType.PlayerCharacter)) {
PlayerCharacter pc = (PlayerCharacter) source;
float focus;
CharacterSkill skill = pc.getSkills().get(effect.getPower().getSkillName());
if (skill == null)
focus = CharacterSkill.getQuickMastery(pc, effect.getPower().getSkillName());
else
focus = skill.getModifiedAmount();
//TODO clean up old formulas once new one is verified
// min *= (0.5 + 0.0075 * pc.getStatIntCurrent() + 0.011 * pc.getStatSpiCurrent() + 0.0196 * focus);
// max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus);
float intt = (pc.getStatIntCurrent() >= 1) ? (float) pc.getStatIntCurrent() : 1f;
float spi = (pc.getStatSpiCurrent() >= 1) ? (float) pc.getStatSpiCurrent() : 1f;
// min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus);
// max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus);
min = HealthEffectModifier.getMinDamage(min, intt, spi, focus);
max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus);
} else if (source.getObjectType() == GameObjectType.Mob) {
Mob pc = (Mob) source;
float focus;
CharacterSkill skill = pc.getSkills().get(effect.getPower().getSkillName());
if (skill == null)
focus = CharacterSkill.getQuickMastery(pc, effect.getPower().getSkillName());
else
focus = skill.getModifiedAmount();
//TODO clean up old formulas once new one is verified
// min *= (0.5 + 0.0075 * pc.getStatIntCurrent() + 0.011 * pc.getStatSpiCurrent() + 0.0196 * focus);
// max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus);
float intt = (pc.getStatIntCurrent() >= 1) ? (float) pc.getStatIntCurrent() : 1f;
if (pc.isPlayerGuard())
intt = 200;
float spi = (pc.getStatSpiCurrent() >= 1) ? (float) pc.getStatSpiCurrent() : 1f;
if (pc.isPlayerGuard())
spi = 200;
// min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus);
// max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus);
min = HealthEffectModifier.getMinDamage(min, intt, spi, focus);
max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus);
//debug for spell damage and atr
// if (pc.getDebug(16)) {
// String smsg = "Damage: " + (int)Math.abs(min) + " - " + (int)Math.abs(max);
// ChatManager.chatSystemInfo(pc, smsg);
// }
}
modAmount = calculateDamage(source, min, max, awo, trains);
PlayerBonuses bonus = source.getBonuses();
// Apply any power effect modifiers (such as stances)
if (bonus != null)
modAmount *= (1 + (bonus.getFloatPercentAll(ModType.PowerDamageModifier, SourceType.None)));
}
if (modAmount == 0f)
return;
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
AbstractCharacter ac = (AbstractCharacter) awo;
if (!ac.isAlive())
return;
int powerID = 0, effectID = 0;
String powerName = "";
if (effect.getPower() != null) {
powerID = effect.getPower().getToken();
powerName = effect.getPower().getName();
} else {
Logger.error("Power has returned null! Damage will fail to register! (" + (ac.getCurrentHitpoints() > 0 ? "Alive)" : "Dead)"));
}
if (effect.getEffect() != null) {
effectID = effect.getEffect().getToken();
} else {
Logger.error("Effect has returned null! Damage will fail to register! (" + (ac.getCurrentHitpoints() > 0 ? "Alive)" : "Dead)"));
}
//see if target is immune to heals
if (modAmount > 0f) {
boolean skipImmune = false;
// first tick of HoT going thru SM was removed in a later patch
/*if (effect.getAction().getPowerAction() instanceof DirectDamagePowerAction) {
ArrayList<ActionsBase> actions = effect.getPower().getActions();
for (ActionsBase ab : actions) {
AbstractPowerAction apa = ab.getPowerAction();
if (apa instanceof DamageOverTimePowerAction)
skipImmune = true;
}
}*/
PlayerBonuses bonus = ac.getBonuses();
if (!skipImmune && bonus.getFloat(ModType.BlackMantle, SourceType.Heal) >= trains) {
ModifyHealthMsg mhm = new ModifyHealthMsg(source, ac, 0f, 0f, 0f, powerID, powerName, trains, effectID);
mhm.setUnknown03(5); //set target is immune
DispatchManager.sendToAllInRange(ac, mhm);
return;
}
}
float mod = 0;
//Modify health
mod = ac.modifyHealth(modAmount, source, false);
float cur = awo.getCurrentHitpoints();
float maxAmount = awo.getHealthMax() - cur;
AbstractNetMsg mhm = null;
if (modAmount < 0 && cur < 0 && mod != 0)
mhm = new ModifyHealthKillMsg(source, ac, modAmount, 0f, 0f, powerID, powerName, trains, effectID);
else
mhm = new ModifyHealthMsg(source, ac, modAmount, 0f, 0f, powerID, powerName, trains, effectID);
if (effect instanceof DamageOverTimeJob) {
if (mhm instanceof ModifyHealthMsg)
((ModifyHealthMsg) mhm).setOmitFromChat(1);
else if (mhm instanceof ModifyHealthKillMsg)
((ModifyHealthKillMsg) mhm).setUnknown02(1);
}
//send the damage
DispatchManager.sendToAllInRange(ac, mhm);
// //send corpse if this kills a mob
// //TODO fix the someone misses blurb.
// if(awo instanceof Mob && awo.getHealth() <= 0) {
// CombatMessageMsg cmm = new CombatMessageMsg(null, 0, awo, 15);
// try {
// DispatchMessage.sendToAllInRange(ac, cmm);
// } catch (Exception e) {
// Logger.error("MobCorpseSendError", e);
// }
// }
} else if (awo.getObjectType().equals(GameObjectType.Building)) {
Building b = (Building) awo;
if (modAmount < 0 && (!b.isVulnerable()))
return; //can't damage invul building
int powerID = 0, effectID = 0;
String powerName = "";
if (effect.getPower() != null) {
powerID = effect.getPower().getToken();
powerName = effect.getPower().getName();
} else
Logger.error("Power has returned null! Damage will fail to register! (" + (b.getRank() == -1 ? "Standing)" : "Destroyed)"));
if (effect.getEffect() != null) {
effectID = effect.getEffect().getToken();
} else
Logger.error("Effect has returned null! Damage will fail to register! (" + (b.getRank() == -1 ? "Standing)" : "Destroyed)"));
float mod = b.modifyHealth(modAmount, source);
ModifyHealthMsg mhm = new ModifyHealthMsg(source, b, modAmount, 0f, 0f, powerID, powerName, trains, effectID);
if (effect instanceof DamageOverTimeJob)
mhm.setOmitFromChat(1);
//send the damage
DispatchManager.sendToAllInRange(b, mhm);
}
}
private float calculateDamage(AbstractCharacter source, float minDamage, float maxDamage, AbstractWorldObject awo, int trains) {
// get range between min and max
float range = maxDamage - minDamage;
// Damage is calculated twice to average a more central point
float damage = ThreadLocalRandom.current().nextFloat() * range;
damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) / 2;
// put it back between min and max
damage += minDamage;
Resists resists = null;
// get resists
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
AbstractCharacter ac = (AbstractCharacter) awo;
resists = ac.getResists();
} else if (awo.getObjectType().equals(GameObjectType.Building))
resists = ((Building) awo).getResists();
// calculate resists in if any
if (resists != null) {
if (AbstractWorldObject.IsAbstractCharacter(awo))
damage = resists.getResistedDamage(source, (AbstractCharacter) awo, damageType, damage * -1, trains) * -1;
else
damage = resists.getResistedDamage(source, null, damageType, damage * -1, trains) * -1;
}
if (AbstractWorldObject.IsAbstractCharacter(awo)) {
AbstractCharacter ac = (AbstractCharacter) awo;
if (ac.isSit())
damage *= 2.5f; // increase damage if sitting
}
return damage;
}
@Override
public void applyBonus(AbstractCharacter ac, int trains) {
}
@Override
public void applyBonus(Item item, int trains) {
}
@Override
public void applyBonus(Building building, int trains) {
}
}