forked from MagicBane/Server
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.
440 lines
15 KiB
440 lines
15 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 int getMaxMinions(Mob guardCaptain) { |
|
|
|
int maxSlots; |
|
|
|
switch (guardCaptain.getRank()) { |
|
case 3: |
|
maxSlots = 2; |
|
break; |
|
case 4: |
|
case 5: |
|
maxSlots = 3; |
|
break; |
|
case 6: |
|
maxSlots = 4; |
|
break; |
|
case 7: |
|
maxSlots = 5; |
|
break; |
|
case 1: |
|
case 2: |
|
default: |
|
maxSlots = 1; |
|
|
|
} |
|
return maxSlots; |
|
} |
|
|
|
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 setDamageAndSpeedForGuard(Mob guard){ |
|
|
|
//handle stance for guard mob |
|
String stancePowerName = ""; |
|
if(guard.agentType.equals(Enum.AIAgentType.GUARDWALLARCHER)){ |
|
//apply rogue offensive stance |
|
stancePowerName = "STC-R-O"; |
|
}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 |
|
stancePowerName = "STC-M-O"; |
|
} else{ |
|
//apply fighter offensive stance |
|
stancePowerName = "STC-F-O"; |
|
} |
|
} |
|
|
|
int stancePowerRank = MobAI.getGuardPowerRank(guard); |
|
PowersBase stancePower = PowersManager.getPowerByIDString(stancePowerName); |
|
PowersManager.useMobPower(guard,guard,stancePower,stancePowerRank); |
|
|
|
if(guard.equip == null) { |
|
guard.minDamageHandOne = (int)(guard.mobBase.getDamageMin()); |
|
guard.maxDamageHandOne = (int)(guard.mobBase.getDamageMax()); |
|
guard.speedHandOne = 30.0f; |
|
}else{ |
|
if(guard.equip.containsKey(1)){ |
|
//has main hand weapon |
|
ItemBase weapon = guard.equip.get(1).getItemBase(); |
|
guard.minDamageHandOne = (int)(guard.mobBase.getDamageMin() + weapon.getMinDamage()); |
|
guard.maxDamageHandOne = (int)(guard.mobBase.getDamageMax() + weapon.getMaxDamage()); |
|
guard.speedHandOne = weapon.getSpeed(); |
|
} else if(guard.equip.containsKey(2) && !guard.equip.get(2).getItemBase().isShield()){ |
|
//has off hand weapon |
|
ItemBase weapon = guard.equip.get(2).getItemBase(); |
|
guard.minDamageHandTwo = (int)(guard.mobBase.getDamageMin() + weapon.getMinDamage()); |
|
guard.maxDamageHandTwo = (int)(guard.mobBase.getDamageMax() + weapon.getMaxDamage()); |
|
guard.speedHandTwo = weapon.getSpeed(); |
|
} else { |
|
guard.minDamageHandOne = (int)(guard.mobBase.getDamageMin()); |
|
guard.maxDamageHandOne = (int)(guard.mobBase.getDamageMax()); |
|
guard.speedHandOne = 30.0f; |
|
} |
|
} |
|
} |
|
public static void setDefenseForGuard(Mob guard){ |
|
|
|
} |
|
|
|
public static void setAttackRatingForGuard(Mob guard){ |
|
|
|
} |
|
|
|
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())); |
|
} |
|
} |
|
}
|
|
|