Public Repository for the Magicbane Shadowbane Emulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

466 lines
18 KiB

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.objects;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.mbEnums.ModType;
import engine.mbEnums.SourceType;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
public class Resists {
private static ConcurrentHashMap<Integer, Resists> mobResists = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private ConcurrentHashMap<mbEnums.DamageType, Float> resists = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private ConcurrentHashMap<mbEnums.DamageType, Boolean> immuneTo = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
private mbEnums.DamageType protection;
private int protectionTrains = 0;
private boolean immuneToAll;
/**
* Generic Constructor
*/
public Resists(String type) {
switch (type) {
case "Building":
setBuildingResists();
break;
case "Mine":
setMineResists();
break;
default:
setGenericResists();
break;
}
}
public Resists(Resists r) {
for (mbEnums.DamageType dt : r.resists.keySet())
this.resists.put(dt, r.resists.get(dt));
for (mbEnums.DamageType dt : r.immuneTo.keySet())
this.immuneTo.put(dt, r.immuneTo.get(dt));
this.protection = r.protection;
this.protectionTrains = r.protectionTrains;
this.immuneToAll = r.immuneToAll;
}
/**
* Generic Constructor for player
*/
public Resists(PlayerCharacter pc) {
setGenericResists();
}
public Resists(Mob mob) {
setGenericResists();
}
/**
* Called for mobBase when getting from the db fails
*/
public Resists(MobBase mobBase) {
setGenericResists();
}
/**
* Database Constructor
*/
public Resists(ResultSet rs) throws SQLException {
this.immuneToAll = false;
this.resists.put(mbEnums.DamageType.SLASHING, rs.getFloat("slash"));
this.resists.put(mbEnums.DamageType.CRUSHING, rs.getFloat("crush"));
this.resists.put(mbEnums.DamageType.PIERCING, rs.getFloat("pierce"));
this.resists.put(mbEnums.DamageType.MAGIC, rs.getFloat("magic"));
this.resists.put(mbEnums.DamageType.BLEEDING, rs.getFloat("bleed"));
this.resists.put(mbEnums.DamageType.POISON, rs.getFloat("poison"));
this.resists.put(mbEnums.DamageType.MENTAL, rs.getFloat("mental"));
this.resists.put(mbEnums.DamageType.HOLY, rs.getFloat("holy"));
this.resists.put(mbEnums.DamageType.UNHOLY, rs.getFloat("unholy"));
this.resists.put(mbEnums.DamageType.LIGHTNING, rs.getFloat("lightning"));
this.resists.put(mbEnums.DamageType.FIRE, rs.getFloat("fire"));
this.resists.put(mbEnums.DamageType.COLD, rs.getFloat("cold"));
this.resists.put(mbEnums.DamageType.HEALING, 0f);
}
//Handle Fortitudes
private static float handleFortitude(AbstractCharacter target, mbEnums.DamageType type, float damage) {
if (target == null || !(target.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter)))
return damage;
PlayerBonuses bonus = target.getBonuses();
//see if there is a fortitude
float damageCap = bonus.getFloatPercentAll(ModType.DamageCap, SourceType.None);
if (damageCap == 0f || type == mbEnums.DamageType.HEALING)
return damage;
//is fortitude, Are we under the cap?
float maxHealth = target.getHealthMax();
float capFire = maxHealth * (damageCap);
if (damage < capFire)
return damage;
//let's see if valid damagetype to apply it
boolean exclusive;
HashSet<SourceType> forts = bonus.getList(ModType.IgnoreDamageCap);
if (forts == null) {
exclusive = true;
forts = bonus.getList(ModType.ExclusiveDamageCap);
} else
exclusive = false;
if (forts == null || !isValidDamageCapType(forts, type, exclusive))
return damage;
float adjustedDamage = bonus.getFloatPercentAll(ModType.AdjustAboveDmgCap, SourceType.None);
//Adjust damage down and return new amount
float aadc = 1 + adjustedDamage;
return capFire * aadc;
}
//Test if Damagetype is valid for foritude
private static boolean isValidDamageCapType(HashSet<SourceType> forts, mbEnums.DamageType damageType, boolean exclusive) {
for (SourceType fort : forts) {
mbEnums.DamageType dt = mbEnums.DamageType.getDamageType(fort.name());
if (dt.equals(mbEnums.DamageType.NONE))
continue;
if (dt.equals(damageType)) {
return exclusive;
}
}
return !exclusive;
}
/**
* Calculate Current Resists for Player
*/
public static void calculateResists(AbstractCharacter ac) {
if (ac.getResists() != null)
ac.getResists().calculateResists(ac, true);
else
Logger.error("Unable to find resists for character " + ac.getObjectUUID());
}
private static float[] getArmorResists(Item armor, float[] phys) {
if (armor == null)
return phys;
if (armor.template.item_type.equals(mbEnums.ItemType.ARMOR)) {
phys[0] += armor.template.combat_attack_resist.get("SLASHING");
phys[1] += armor.template.combat_attack_resist.get("CRUSHING");
phys[2] += armor.template.combat_attack_resist.get("PIERCING");
}
return phys;
}
/**
* Get mob resists from db if there, otherwise set defaults
*/
public static Resists getResists(int resistID) {
//check cache first
if (mobResists.containsKey(resistID))
return new Resists(mobResists.get(resistID));
//get from database
Resists resists = DbManager.ResistQueries.GET_RESISTS_FOR_MOB(resistID);
if (resists != null) {
mobResists.put(resistID, resists);
return new Resists(resists);
}
//failed, may want to debug this
return null;
}
/**
* Create generic resists for buildings
*/
public final void setBuildingResists() {
this.immuneToAll = false;
this.resists.put(mbEnums.DamageType.SLASHING, 85f);
this.resists.put(mbEnums.DamageType.CRUSHING, 85f);
this.resists.put(mbEnums.DamageType.SIEGE, 0f);
this.immuneTo.put(mbEnums.DamageType.PIERCING, true);
this.immuneTo.put(mbEnums.DamageType.MAGIC, true);
this.immuneTo.put(mbEnums.DamageType.BLEEDING, true);
this.immuneTo.put(mbEnums.DamageType.POISON, true);
this.immuneTo.put(mbEnums.DamageType.MENTAL, true);
this.immuneTo.put(mbEnums.DamageType.HOLY, true);
this.immuneTo.put(mbEnums.DamageType.UNHOLY, true);
this.immuneTo.put(mbEnums.DamageType.LIGHTNING, true);
this.immuneTo.put(mbEnums.DamageType.FIRE, true);
this.immuneTo.put(mbEnums.DamageType.COLD, true);
}
/**
* Create generic resists for mines
*/
public final void setMineResists() {
this.immuneToAll = false;
this.immuneTo.put(mbEnums.DamageType.SLASHING, true);
this.immuneTo.put(mbEnums.DamageType.CRUSHING, true);
this.immuneTo.put(mbEnums.DamageType.PIERCING, true);
this.immuneTo.put(mbEnums.DamageType.MAGIC, true);
this.immuneTo.put(mbEnums.DamageType.BLEEDING, true);
this.immuneTo.put(mbEnums.DamageType.POISON, true);
this.immuneTo.put(mbEnums.DamageType.MENTAL, true);
this.immuneTo.put(mbEnums.DamageType.HOLY, true);
this.immuneTo.put(mbEnums.DamageType.UNHOLY, true);
this.immuneTo.put(mbEnums.DamageType.LIGHTNING, true);
this.immuneTo.put(mbEnums.DamageType.FIRE, true);
this.immuneTo.put(mbEnums.DamageType.COLD, true);
this.resists.put(mbEnums.DamageType.SIEGE, 0f);
}
/**
* Create generic resists
*/
public final void setGenericResists() {
this.immuneToAll = false;
this.resists.put(mbEnums.DamageType.SLASHING, 0f);
this.resists.put(mbEnums.DamageType.CRUSHING, 0f);
this.resists.put(mbEnums.DamageType.PIERCING, 0f);
this.resists.put(mbEnums.DamageType.MAGIC, 0f);
this.resists.put(mbEnums.DamageType.BLEEDING, 0f);
this.resists.put(mbEnums.DamageType.POISON, 0f);
this.resists.put(mbEnums.DamageType.MENTAL, 0f);
this.resists.put(mbEnums.DamageType.HOLY, 0f);
this.resists.put(mbEnums.DamageType.UNHOLY, 0f);
this.resists.put(mbEnums.DamageType.LIGHTNING, 0f);
this.resists.put(mbEnums.DamageType.FIRE, 0f);
this.resists.put(mbEnums.DamageType.COLD, 0f);
this.resists.put(mbEnums.DamageType.HEALING, 0f);
this.immuneTo.put(mbEnums.DamageType.SIEGE, true);
}
/**
* Get a resist
*/
public float getResist(mbEnums.DamageType type, int trains) {
//get resisted amount
Float amount = 0f;
if (this.resists.containsKey(type))
amount = this.resists.get(type);
//add protection
if (trains > 0 && protection != null && type.equals(this.protection)) {
float prot = 50 + this.protectionTrains - trains;
amount += (prot >= 0) ? prot : 0;
}
if (amount == null)
return 0f;
if (amount > 75f)
return 75f;
return amount;
}
/**
* get immuneTo
*/
public boolean immuneTo(mbEnums.DamageType type) {
if (this.immuneTo.containsKey(type))
return this.immuneTo.get(type);
else
return false;
}
/**
* get immuneToAll
*/
public boolean immuneToAll() {
return this.immuneToAll;
}
public boolean immuneToAttacks() {
return immuneTo(mbEnums.DamageType.ATTACK);
}
/**
* Set a resist
*/
public void setResist(mbEnums.DamageType type, float value) {
this.resists.put(type, value);
}
/**
* set immuneToAll
*/
public void setImmuneToAll(boolean value) {
this.immuneToAll = value;
}
/**
* set resists from mobbase
*/
public void setMobResists(int resistID) {
//TODO add this in later
//calls `static_npc_mob_resists` table WHERE `ID`='resistID'
}
/**
* get Damage after resist
* Expects heals as negative damage and damage as positive damage for fortitudes.
*/
public float getResistedDamage(AbstractCharacter source, AbstractCharacter target, mbEnums.DamageType type, float damage, int trains) {
//handle fortitudes
damage = handleFortitude(target, type, damage);
//calculate armor piercing
float ap = source.getBonuses().getFloatPercentAll(ModType.ArmorPiercing, SourceType.None);
float damageAfterResists = damage * (1 - (this.getResist(type, trains) * 0.01f) + ap);
//check to see if any damage absorbers should cancel
if (target != null)
target.cancelOnTakeDamage(type, (damageAfterResists));
return damageAfterResists;
}
public void calculateResists(AbstractCharacter ac, boolean val) {
this.immuneTo.clear();
// get resists for runes
PlayerBonuses rb = ac.getBonuses();
float slash = 0f, crush = 0f, pierce = 0f, magic = 0f, bleed = 0f, mental = 0f, holy = 0f, unholy = 0f, poison = 0f, lightning = 0f, fire = 0f, cold = 0f, healing = 0f;
if (rb != null) {
// Handle immunities
if (rb.getBool(ModType.ImmuneTo, SourceType.Stun))
this.immuneTo.put(mbEnums.DamageType.STUN, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.Blind))
this.immuneTo.put(mbEnums.DamageType.BLINDNESS, true);
if (rb.getBool(ModType.ImmuneToAttack, SourceType.None))
this.immuneTo.put(mbEnums.DamageType.ATTACK, true);
if (rb.getBool(ModType.ImmuneToPowers, SourceType.None))
this.immuneTo.put(mbEnums.DamageType.POWERS, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.Powerblock))
this.immuneTo.put(mbEnums.DamageType.POWERINHIBITOR, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.DeBuff))
this.immuneTo.put(mbEnums.DamageType.DEBUFF, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.Fear))
this.immuneTo.put(mbEnums.DamageType.FEAR, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.Charm))
this.immuneTo.put(mbEnums.DamageType.CHARM, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.Root))
this.immuneTo.put(mbEnums.DamageType.ROOT, true);
if (rb.getBool(ModType.ImmuneTo, SourceType.Snare))
this.immuneTo.put(mbEnums.DamageType.SNARE, true);
// Handle resists
slash += rb.getFloat(ModType.Resistance, SourceType.Slashing);
crush += rb.getFloat(ModType.Resistance, SourceType.Crushing);
pierce += rb.getFloat(ModType.Resistance, SourceType.Piercing);
magic += rb.getFloat(ModType.Resistance, SourceType.Magic);
bleed += rb.getFloat(ModType.Resistance, SourceType.Bleeding);
poison += rb.getFloat(ModType.Resistance, SourceType.Poison);
mental += rb.getFloat(ModType.Resistance, SourceType.Mental);
holy += rb.getFloat(ModType.Resistance, SourceType.Holy);
unholy += rb.getFloat(ModType.Resistance, SourceType.Unholy);
lightning += rb.getFloat(ModType.Resistance, SourceType.Lightning);
fire += rb.getFloat(ModType.Resistance, SourceType.Fire);
cold += rb.getFloat(ModType.Resistance, SourceType.Cold);
healing += rb.getFloat(ModType.Resistance, SourceType.Healing); // DamageType.Healing.name());
}
// get resists from equipment
if (ac.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter)) {
if (ac.charItemManager != null && ac.charItemManager.getEquipped() != null) {
float[] phys = {0f, 0f, 0f};
ConcurrentHashMap<mbEnums.EquipSlotType, Item> equip = ac.charItemManager.getEquipped();
// get base physical resists
phys = Resists.getArmorResists(equip.get(mbEnums.EquipSlotType.HELM), phys);
phys = Resists.getArmorResists(equip.get(mbEnums.EquipSlotType.CHEST), phys);
phys = Resists.getArmorResists(equip.get(mbEnums.EquipSlotType.UPARM), phys);
phys = Resists.getArmorResists(equip.get(mbEnums.EquipSlotType.HANDS), phys);
phys = Resists.getArmorResists(equip.get(mbEnums.EquipSlotType.LEGS), phys);
phys = Resists.getArmorResists(equip.get(mbEnums.EquipSlotType.FEET), phys);
slash += phys[0];
crush += phys[1];
pierce += phys[2];
}
}
this.resists.put(mbEnums.DamageType.SLASHING, slash);
this.resists.put(mbEnums.DamageType.CRUSHING, crush);
this.resists.put(mbEnums.DamageType.PIERCING, pierce);
this.resists.put(mbEnums.DamageType.MAGIC, magic);
this.resists.put(mbEnums.DamageType.BLEEDING, bleed);
this.resists.put(mbEnums.DamageType.POISON, poison);
this.resists.put(mbEnums.DamageType.MENTAL, mental);
this.resists.put(mbEnums.DamageType.HOLY, holy);
this.resists.put(mbEnums.DamageType.UNHOLY, unholy);
this.resists.put(mbEnums.DamageType.LIGHTNING, lightning);
this.resists.put(mbEnums.DamageType.FIRE, fire);
this.resists.put(mbEnums.DamageType.COLD, cold);
this.resists.put(mbEnums.DamageType.HEALING, healing);
this.immuneTo.put(mbEnums.DamageType.SIEGE, true);
// debug printing of resists
// printResists(pc);
}
public void printResistsToClient(PlayerCharacter pc) {
for (mbEnums.DamageType dt : resists.keySet())
ChatManager.chatSystemInfo(pc, " resist." + dt.name() + ": " + resists.get(dt));
for (mbEnums.DamageType dt : immuneTo.keySet())
ChatManager.chatSystemInfo(pc, " immuneTo." + dt.name() + ": " + immuneTo.get(dt));
ChatManager.chatSystemInfo(pc, " immuneToAll: " + this.immuneToAll);
if (protection != null)
ChatManager.chatSystemInfo(pc, " Protection: " + protection.name() + ", Trains: " + protectionTrains);
else
ChatManager.chatSystemInfo(pc, " Protection: None");
}
public String getResists(PlayerCharacter pc) {
String out = pc.getName();
out += "Resists: ";
Iterator<mbEnums.DamageType> it = this.resists.keySet().iterator();
while (it.hasNext()) {
mbEnums.DamageType damType = it.next();
String dtName = damType.name();
out += dtName + '=' + this.resists.get(dtName) + ", ";
}
out += "ImmuneTo: ";
it = this.immuneTo.keySet().iterator();
while (it.hasNext()) {
mbEnums.DamageType damType = it.next();
String dtName = damType.name();
out += dtName + '=' + this.resists.get(dtName) + ", ";
}
if (protection != null)
out += "Protection: " + protection.name() + ", Trains: " + protectionTrains;
else
out += "Protection: none";
return out;
}
}