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.

475 lines
18 KiB

package engine.gameManager;
import engine.Enum;
import engine.InterestManagement.WorldGrid;
import engine.math.Quaternion;
import engine.math.Vector3f;
import engine.math.Vector3fImmutable;
import engine.mobileAI.MobAI;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
import engine.net.client.msg.PetMsg;
import engine.objects.*;
import engine.powers.EffectsBase;
import engine.powers.PowersBase;
import engine.powers.RuneSkillAdjustEntry;
import org.pmw.tinylog.Logger;
import javax.smartcardio.ATR;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ThreadLocalRandom;
import static engine.math.FastMath.acos;
public enum NPCManager {
NPC_MANAGER;
public static HashMap<Integer, ArrayList<Integer>> _runeSetMap = new HashMap<>();
public static void dismissNecroPet(Mob necroPet, boolean updateOwner) {
necroPet.setCombatTarget(null);
necroPet.hasLoot = false;
if (necroPet.parentZone != null)
necroPet.parentZone.zoneMobSet.remove(necroPet);
try {
necroPet.clearEffects();
} catch (Exception e) {
Logger.error(e.getMessage());
}
necroPet.playerAgroMap.clear();
WorldGrid.RemoveWorldObject(necroPet);
DbManager.removeFromCache(necroPet);
PlayerCharacter petOwner = (PlayerCharacter) necroPet.guardCaptain;
if (petOwner != null) {
necroPet.guardCaptain = null;
petOwner.setPet(null);
if (updateOwner == false)
return;
PetMsg petMsg = new PetMsg(5, null);
Dispatch dispatch = Dispatch.borrow(petOwner, petMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
}
}
public static void auditNecroPets(PlayerCharacter player) {
int removeIndex = 0;
while (player.necroPets.size() >= 10) {
if (removeIndex == player.necroPets.size())
break;
Mob necroPet = player.necroPets.get(removeIndex);
if (necroPet == null) {
removeIndex++;
continue;
}
dismissNecroPet(necroPet, true);
player.necroPets.remove(necroPet);
removeIndex++;
}
}
public static void resetNecroPets(PlayerCharacter player) {
for (Mob necroPet : player.necroPets)
if (necroPet.isPet())
necroPet.agentType = Enum.AIAgentType.MOBILE;
}
public static void spawnNecroPet(PlayerCharacter playerCharacter, Mob mob) {
if (mob == null)
return;
if (mob.getMobBaseID() != 12021 && mob.getMobBaseID() != 12022)
return;
auditNecroPets(playerCharacter);
resetNecroPets(playerCharacter);
playerCharacter.necroPets.add(mob);
}
public static void dismissNecroPets(PlayerCharacter playerCharacter) {
if (playerCharacter.necroPets.isEmpty())
return;
for (Mob necroPet : playerCharacter.necroPets) {
try {
dismissNecroPet(necroPet, true);
} catch (Exception e) {
Logger.error(e);
}
}
playerCharacter.necroPets.clear();
}
public static void loadAllPirateNames() {
DbManager.NPCQueries.LOAD_PIRATE_NAMES();
}
public static String getPirateName(int mobBaseID) {
ArrayList<String> nameList = null;
// If we cannot find name for this mobbase then
// fallback to human male
if (NPC._pirateNames.containsKey(mobBaseID))
nameList = NPC._pirateNames.get(mobBaseID);
else
nameList = NPC._pirateNames.get(2111);
if (nameList == null) {
Logger.error("Null name list for 2111!");
}
return nameList.get(ThreadLocalRandom.current().nextInt(nameList.size()));
}
public static ArrayList<Building> getProtectedBuildings(NPC npc) {
ArrayList<Building> protectedBuildings = new ArrayList<>();
if (npc.building == null)
return protectedBuildings;
if (npc.building.getCity() == null)
return protectedBuildings;
for (Building b : npc.building.getCity().getParent().zoneBuildingSet) {
if (b.getBlueprint() == null)
continue;
if (b.getProtectionState().equals(Enum.ProtectionState.CONTRACT))
protectedBuildings.add(b);
if (b.getProtectionState().equals(Enum.ProtectionState.PENDING))
protectedBuildings.add(b);
}
return protectedBuildings;
}
public static int slotCharacterInBuilding(AbstractCharacter abstractCharacter) {
int buildingSlot;
if (abstractCharacter.building == null)
return -1;
// Get next available slot for this NPC and use it
// to add the NPC to the building's hireling list
// Account for R8's having slots reversed.
if (abstractCharacter.building.getBlueprint() != null && abstractCharacter.building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.TOL) && abstractCharacter.building.getRank() == 8)
buildingSlot = BuildingManager.getLastAvailableSlot(abstractCharacter.building);
else
buildingSlot = BuildingManager.getAvailableSlot(abstractCharacter.building);
// Override slot for siege engines
if (abstractCharacter.getObjectType().equals(Enum.GameObjectType.Mob) && ((Mob) abstractCharacter).behaviourType.equals(Enum.MobBehaviourType.SiegeEngine)) {
Mob siegeMobile = (Mob) abstractCharacter;
buildingSlot = siegeMobile.guardCaptain.minions.size() + 2;
}
if (buildingSlot == -1)
Logger.error("No available slot for NPC: " + abstractCharacter.getObjectUUID());
// Pets are regular mobiles not hirelings (Siege engines)
if (abstractCharacter.contract != null)
abstractCharacter.building.getHirelings().put(abstractCharacter, buildingSlot);
// Override bind and location for this npc derived
// from BuildingManager slot location data.
Vector3fImmutable slotLocation = BuildingManager.getSlotLocation(abstractCharacter.building, buildingSlot).getLocation();
abstractCharacter.bindLoc = abstractCharacter.building.getLoc().add(slotLocation);
// Rotate slot position by the building rotation
abstractCharacter.bindLoc = Vector3fImmutable.rotateAroundPoint(abstractCharacter.building.getLoc(), abstractCharacter.bindLoc, abstractCharacter.building.getBounds().getQuaternion().angleY);
abstractCharacter.loc = new Vector3fImmutable(abstractCharacter.bindLoc);
// Rotate NPC rotation by the building's rotation
Quaternion slotRotation = new Quaternion().fromAngles(0, acos(abstractCharacter.getRot().y) * 2, 0);
slotRotation = slotRotation.mult(abstractCharacter.building.getBounds().getQuaternion());
abstractCharacter.setRot(new Vector3f(0, slotRotation.y, 0));
// Configure region and floor/level for this NPC
abstractCharacter.region = BuildingManager.GetRegion(abstractCharacter.building, abstractCharacter.bindLoc.x, abstractCharacter.bindLoc.y, abstractCharacter.bindLoc.z);
return buildingSlot;
}
public static void AssignPatrolPoints(Mob mob) {
mob.patrolPoints = new ArrayList<>();
for (int i = 0; i < 5; ++i) {
float patrolRadius = mob.getSpawnRadius();
if (patrolRadius > 256)
patrolRadius = 256;
if (patrolRadius < 60)
patrolRadius = 60;
Vector3fImmutable newPatrolPoint = Vector3fImmutable.getRandomPointInCircle(mob.getBindLoc(), patrolRadius);
mob.patrolPoints.add(newPatrolPoint);
if (i == 1) {
mob.setLoc(newPatrolPoint);
mob.endLoc = newPatrolPoint;
}
}
}
public static void applyGuardStanceModifiers(Mob guard){
float damageModifier = 1;
float attackRatingModifier = 1;
float defenseModifier = 1;
float attackSpeedModifier = 1;
float powerDamageModifier = 1;
//handle stance modifiers for guard mob
if(guard.agentType.equals(Enum.AIAgentType.GUARDWALLARCHER)){
//apply rogue bonuses
attackRatingModifier += 0.5f;
defenseModifier += 0.5f;
damageModifier += 0.5f;
attackSpeedModifier -= 0.36f;
}else {
Integer contractID;
if (guard.agentType.equals(Enum.AIAgentType.GUARDMINION)) {
contractID = guard.guardCaptain.contract.getContractID();
} else{
contractID = guard.contract.getContractID();
}
if (Enum.MinionType.ContractToMinionMap.get(contractID) != null && Enum.MinionType.ContractToMinionMap.get(contractID).isMage()){
//apply mage offensive Stance
powerDamageModifier += 0.5f;
} else{
//apply fighter offensive stance
damageModifier += 0.5f;
attackSpeedModifier -= 0.36f;
}
}
guard.minDamageHandOne *= damageModifier;
guard.minDamageHandTwo *= damageModifier;
guard.maxDamageHandOne *= damageModifier;
guard.maxDamageHandTwo *= damageModifier;
guard.atrHandOne *= attackRatingModifier;
guard.atrHandTwo *= attackRatingModifier;
guard.defenseRating *= defenseModifier;
guard.speedHandOne *= attackSpeedModifier;
guard.speedHandTwo *= attackSpeedModifier;
//TODO figure out how to apply +50% powerdamage to mage guards
}
public static void setDamageAndSpeedForGuard(Mob guard){
float rankModifier = 1 + (guard.getRank() * 0.1f);
int primaryStat = 0;
if(guard.equip == null) {
guard.minDamageHandOne = (int)((guard.mobBase.getDamageMin()) * rankModifier);
guard.maxDamageHandOne = (int)((guard.mobBase.getDamageMax()) * rankModifier);
guard.speedHandOne = 30.0f;
}else{
if(guard.equip.containsKey(1)){
//has main hand weapon
ItemBase weapon = guard.equip.get(1).getItemBase();
if(weapon.isStrBased())
primaryStat = guard.getStatStrCurrent();
else
primaryStat = guard.getStatDexCurrent();
guard.minDamageHandOne = (int)((guard.mobBase.getDamageMin() + weapon.getMinDamage()) * rankModifier) + primaryStat;
guard.maxDamageHandOne = (int)((guard.mobBase.getDamageMax() + weapon.getMaxDamage()) * rankModifier) + primaryStat;
guard.speedHandOne = weapon.getSpeed();
guard.rangeHandOne = weapon.getRange();
} else if(guard.equip.containsKey(2) && !guard.equip.get(2).getItemBase().isShield()){
//has off hand weapon
ItemBase weapon = guard.equip.get(2).getItemBase();
if(weapon.isStrBased())
primaryStat = guard.getStatStrCurrent();
else
primaryStat = guard.getStatDexCurrent();
guard.minDamageHandTwo = (int)((guard.mobBase.getDamageMin() + weapon.getMinDamage()) * rankModifier) + primaryStat;
guard.maxDamageHandTwo = (int)((guard.mobBase.getDamageMax() + weapon.getMaxDamage()) * rankModifier) + primaryStat;
guard.speedHandTwo = weapon.getSpeed();
guard.rangeHandTwo = weapon.getRange();
} else {
primaryStat = guard.getStatStrCurrent();
guard.minDamageHandOne = (int)((guard.mobBase.getDamageMin()) * rankModifier) + primaryStat;
guard.maxDamageHandOne = (int)((guard.mobBase.getDamageMax()) * rankModifier) + primaryStat;
guard.speedHandOne = 30.0f;
guard.rangeHandOne = 3;
}
}
}
public static void setDefenseForGuard(Mob guard){
int dexterity = guard.getStatDexCurrent();
if(dexterity < 1)
dexterity = 1;
int baseDef = guard.mobBase.getDefenseRating();
int armorDefense = 0;
for(MobEquipment equipped : guard.equip.values())
if(equipped.getItemBase().isArmor() || equipped.getItemBase().isShield())
armorDefense += equipped.getItemBase().getDefense();
guard.defenseRating = dexterity + baseDef + armorDefense;
}
public static void setAttackRatingForGuard(Mob guard) {
int strength = guard.getStatStrCurrent();
int baseAtr = guard.mobBase.getAttackRating();
if (guard.equip.get(1) != null)
guard.atrHandOne = baseAtr + (int) ((strength * 0.5f) + (guard.equip.get(1).getItemBase().getPercentRequired() * 4) + (guard.equip.get(1).getItemBase().getPercentRequired() * 3));
else if (guard.equip.get(2) != null && !guard.equip.get(2).getItemBase().isShield())
guard.atrHandTwo = baseAtr + (int) ((strength * 0.5f) + (guard.equip.get(2).getItemBase().getPercentRequired() * 4) + (guard.equip.get(2).getItemBase().getPercentRequired() * 3));
else
guard.atrHandOne = baseAtr;
}
public static void setMaxHealthForGuard(Mob guard){
//values derived fom reading memory address for health on client when selecting player guards
switch(guard.getRank()){
default:
guard.healthMax = 750; //rank 1
break;
case 2:
guard.healthMax = 2082;
break;
case 3:
guard.healthMax = 2740;
break;
case 4:
guard.healthMax = 3414;
break;
case 5:
guard.healthMax = 4080;
break;
case 6:
guard.healthMax = 4746;
break;
case 7:
guard.healthMax = 5412;
break;
}
}
public static void applyMobbaseEffects(Mob mob) {
EffectsBase effectsBase;
for (MobBaseEffects mbe : mob.mobBase.effectsList) {
effectsBase = PowersManager.getEffectByToken(mbe.getToken());
if (effectsBase == null) {
Logger.info("Mob: " + mob.getObjectUUID() + " EffectsBase Null for Token " + mbe.getToken());
continue;
}
//check to upgrade effects if needed.
if (mob.effects.containsKey(Integer.toString(effectsBase.getUUID()))) {
if (mbe.getReqLvl() > (int) mob.level)
continue;
Effect eff = mob.effects.get(Integer.toString(effectsBase.getUUID()));
if (eff == null)
continue;
//Current effect is a higher rank, dont apply.
if (eff.getTrains() > mbe.getRank())
continue;
//new effect is of a higher rank. remove old effect and apply new one.
eff.cancelJob();
mob.addEffectNoTimer(Integer.toString(effectsBase.getUUID()), effectsBase, mbe.getRank(), true);
} else {
if (mbe.getReqLvl() > (int) mob.level)
continue;
mob.addEffectNoTimer(Integer.toString(effectsBase.getUUID()), effectsBase, mbe.getRank(), true);
}
}
}
public static void applyEquipmentResists(Mob mob){
if(mob.equip != null){
for(MobEquipment equipped : mob.equip.values()){
ItemBase itemBase = equipped.getItemBase();
if(itemBase.isHeavyArmor() || itemBase.isLightArmor() || itemBase.isMediumArmor()){
mob.resists.setResist(Enum.DamageType.Crush, mob.resists.getResist(Enum.DamageType.Crush,0) + itemBase.getCrushResist());
mob.resists.setResist(Enum.DamageType.Slash, mob.resists.getResist(Enum.DamageType.Slash,0) + itemBase.getCrushResist());
mob.resists.setResist(Enum.DamageType.Pierce, mob.resists.getResist(Enum.DamageType.Pierce,0) + itemBase.getCrushResist());
}
}
}
}
public static void applyMobbaseSkill(Mob mob) {
SkillsBase baseSkill = DbManager.SkillsBaseQueries.GET_BASE_BY_TOKEN(mob.mobBase.getMobBaseStats().getBaseSkill());
if(baseSkill != null)
mob.getSkills().put(baseSkill.getName(),new CharacterSkill(baseSkill,mob,mob.mobBase.getMobBaseStats().getBaseSkillAmount()));
}
public static void applyRuneSkills(Mob mob, int runeID){
//load mob skill adjustments from mobbase rune
if(PowersManager._allRuneSkillAdjusts.containsKey(runeID))
for(RuneSkillAdjustEntry entry : PowersManager._allRuneSkillAdjusts.get(runeID)) {
if(SkillsBase.getFromCache(entry.skill_type) == null)
SkillsBase.putInCache(DbManager.SkillsBaseQueries.GET_BASE_BY_NAME(entry.skill_type));
SkillsBase skillBase = SkillsBase.getFromCache(entry.skill_type);
if(skillBase == null)
continue;
if (entry.level <= mob.level)
if (mob.skills.containsKey(entry.name) == false)
mob.skills.put(entry.skill_type, new CharacterSkill(skillBase, mob, entry.rank));
else
mob.skills.put(entry.skill_type, new CharacterSkill(skillBase, mob, entry.rank + mob.skills.get(entry.skill_type).getNumTrains()));
}
}
public static void applyRunesForNPC(NPC npc){
for(int runeID : _runeSetMap.get(npc.runeSetID)){
RuneBase rb = RuneBase.getRuneBase(runeID);
CharacterRune toApply = new CharacterRune(rb,npc.getObjectUUID());
npc.runes.add(toApply);
EffectsBase effectsBase;
for(MobBaseEffects effect : toApply.getRuneBase().getEffectsList()){
effectsBase = PowersManager.getEffectByToken(effect.getToken());
npc.addEffectNoTimer(Integer.toString(effectsBase.getUUID()), effectsBase, effect.getRank(), true);
}
}
}
}