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.
703 lines
22 KiB
703 lines
22 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
|
|
package engine.gameManager; |
|
|
|
import engine.Enum; |
|
import engine.Enum.BuildingGroup; |
|
import engine.Enum.GameObjectType; |
|
import engine.InterestManagement.WorldGrid; |
|
import engine.job.JobContainer; |
|
import engine.job.JobScheduler; |
|
import engine.jobs.UpgradeBuildingJob; |
|
import engine.math.Bounds; |
|
import engine.math.Quaternion; |
|
import engine.math.Vector3fImmutable; |
|
import engine.net.client.msg.ErrorPopupMsg; |
|
import engine.objects.*; |
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.time.LocalDateTime; |
|
import java.time.ZoneId; |
|
import java.util.ArrayList; |
|
import java.util.HashMap; |
|
import java.util.concurrent.ThreadLocalRandom; |
|
|
|
public enum BuildingManager { |
|
|
|
BUILDINGMANAGER; |
|
|
|
public static HashMap<Integer, ArrayList<BuildingLocation>> _stuckLocations = new HashMap<>(); |
|
public static HashMap<Integer, ArrayList<BuildingLocation>> _slotLocations = new HashMap<>(); |
|
|
|
public static int getAvailableSlot(Building building) { |
|
|
|
ArrayList<BuildingLocation> slotLocations = _slotLocations.get(building.meshUUID); |
|
|
|
// Some meshes might not have slot locations assigned. |
|
|
|
if (slotLocations == null || |
|
slotLocations.isEmpty()) |
|
return -1; |
|
|
|
int numOfSlots = _slotLocations.get(building.meshUUID).size(); |
|
|
|
for (int i = 1; i <= numOfSlots; i++) { |
|
|
|
if (!building.getHirelings().containsValue(i)) |
|
return i; |
|
} |
|
return -1; |
|
} |
|
|
|
public static BuildingLocation getSlotLocation(Building building, int slot) { |
|
|
|
BuildingLocation buildingLocation = new BuildingLocation(); |
|
|
|
if (slot == -1) |
|
return buildingLocation; |
|
|
|
buildingLocation = _slotLocations.get(building.meshUUID).get(slot - 1); // array index |
|
|
|
if (buildingLocation == null) { |
|
Logger.error("Invalid slot for building: " + building.getObjectUUID()); |
|
} |
|
|
|
return buildingLocation; |
|
} |
|
|
|
public static Quaternion getSlotRotation(Building building, int slot) { |
|
|
|
if (slot == -1) |
|
return new Quaternion(); |
|
|
|
BuildingLocation buildingLocation; |
|
buildingLocation = _slotLocations.get(building.meshUUID).get(slot - 1); // array index |
|
|
|
if (buildingLocation == null) { |
|
Logger.error("Invalid slot rotation for building: " + building.getObjectUUID()); |
|
} |
|
|
|
return buildingLocation.getRotation(); |
|
} |
|
|
|
public static boolean playerCanManage(PlayerCharacter player, Building building) { |
|
|
|
if (player == null) |
|
return false; |
|
|
|
if (building == null) |
|
return false; |
|
|
|
|
|
if (building.getRank() == -1) |
|
return false; |
|
|
|
if (IsOwner(building, player)) |
|
return true; |
|
|
|
//individual friend. |
|
if (building.getFriends().get(player.getObjectUUID()) != null) |
|
return true; |
|
|
|
//Admin's can access stuff |
|
|
|
if (player.isCSR()) |
|
return true; |
|
|
|
//Guild stuff |
|
|
|
|
|
if (building.getGuild() != null && building.getGuild().isGuildLeader(player.getObjectUUID())) |
|
return true; |
|
|
|
if (building.getFriends().get(player.getGuild().getObjectUUID()) != null |
|
&& building.getFriends().get(player.getGuild().getObjectUUID()).getFriendType() == 8) |
|
return true; |
|
|
|
if (building.getFriends().get(player.getGuild().getObjectUUID()) != null |
|
&& building.getFriends().get(player.getGuild().getObjectUUID()).getFriendType() == 9 |
|
&& GuildStatusController.isInnerCouncil(player.getGuildStatus())) |
|
return true; |
|
|
|
if (Guild.sameGuild(building.getGuild(), player.getGuild()) && GuildStatusController.isInnerCouncil(player.getGuildStatus())) |
|
return true; |
|
|
|
if (Guild.sameGuild(building.getGuild(), player.getGuild()) && GuildStatusController.isGuildLeader(player.getGuildStatus())) |
|
return true; |
|
|
|
return false; |
|
|
|
//TODO test friends list once added |
|
//does not meet above criteria. Cannot access. |
|
} |
|
|
|
public static boolean playerCanManageNotFriends(PlayerCharacter player, Building building) { |
|
|
|
//Player Can only Control Building if player is in Same Guild as Building and is higher rank than IC. |
|
|
|
if (player == null) |
|
return false; |
|
|
|
if (building == null) |
|
return false; |
|
|
|
|
|
if (building.getRank() == -1) |
|
return false; |
|
|
|
if (IsOwner(building, player)) |
|
return true; |
|
|
|
//Somehow guild leader check fails? lets check if Player is true Guild GL. |
|
if (building.getGuild() != null && building.getGuild().isGuildLeader(player.getObjectUUID())) |
|
return true; |
|
if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) |
|
return false; |
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
public static synchronized boolean lootBuilding(PlayerCharacter player, Building building) { |
|
|
|
if (building == null) |
|
return false; |
|
|
|
if (player == null) |
|
return false; |
|
|
|
if (building.getRank() != -1) |
|
return false; |
|
|
|
if (building.getBlueprintUUID() == 0) |
|
return false; |
|
|
|
switch (building.getBlueprint().getBuildingGroup()) { |
|
case SHRINE: |
|
Shrine shrine = Shrine.shrinesByBuildingUUID.get(building.getObjectUUID()); |
|
if (shrine == null) |
|
return false; |
|
|
|
int amount = shrine.getFavors(); |
|
//no more favors too loot! |
|
if (amount == 0) { |
|
|
|
try { |
|
ErrorPopupMsg.sendErrorPopup(player, 166); |
|
} catch (Exception e) { |
|
|
|
} |
|
return false; |
|
} |
|
|
|
ItemBase elanIB = ItemBase.getItemBase(1705032); |
|
|
|
if (elanIB == null) |
|
return false; |
|
|
|
if (!player.getCharItemManager().hasRoomInventory(elanIB.getWeight())) |
|
return false; |
|
|
|
if (!Item.MakeItemForPlayer(elanIB, player, amount)) |
|
return false; |
|
|
|
shrine.setFavors(0); |
|
break; |
|
case WAREHOUSE: |
|
|
|
Warehouse warehouse = Warehouse.warehouseByBuildingUUID.get(building.getObjectUUID()); |
|
|
|
if (warehouse == null) |
|
return false; |
|
|
|
for (ItemBase resourceBase : ItemBase.getResourceList()) { |
|
if (!player.getCharItemManager().hasRoomInventory(resourceBase.getWeight())) { |
|
ChatManager.chatSystemInfo(player, "You can not carry any more of that item."); |
|
return false; |
|
} |
|
if (warehouse.getResources().get(resourceBase) == null) |
|
continue; |
|
|
|
int resourceAmount = warehouse.getResources().get(resourceBase); |
|
|
|
if (resourceAmount <= 0) |
|
continue; |
|
|
|
if (warehouse.loot(player, resourceBase, resourceAmount, true)) |
|
ChatManager.chatInfoInfo(player, "You have looted " + resourceAmount + ' ' + resourceBase.getName()); |
|
} |
|
break; |
|
|
|
} |
|
//Everything was looted, Maybe we should |
|
return true; |
|
} |
|
|
|
//This method restarts an upgrade timer when a building is loaded from the database. |
|
// Submit upgrade job for this building based upon it's current upgradeDateTime |
|
|
|
public static void submitUpgradeJob(Building building) { |
|
|
|
if (building == null) |
|
return; |
|
|
|
|
|
if (building.getUpgradeDateTime() == null) { |
|
Logger.error("Attempt to submit upgrade job for non-ranking building"); |
|
return; |
|
} |
|
|
|
// Submit upgrade job for future date or current instant |
|
|
|
if (building.getUpgradeDateTime().isAfter(LocalDateTime.now())) { |
|
JobContainer jc = JobScheduler.getInstance().scheduleJob(new UpgradeBuildingJob(building), |
|
building.getUpgradeDateTime().atZone(ZoneId.systemDefault()) |
|
.toInstant().toEpochMilli()); |
|
} else |
|
JobScheduler.getInstance().scheduleJob(new UpgradeBuildingJob(building), 0); |
|
} |
|
|
|
public static void setUpgradeDateTime(Building building, LocalDateTime upgradeDateTime, int rankCost) { |
|
|
|
if (building == null) |
|
return; |
|
if (!DbManager.BuildingQueries.updateBuildingUpgradeTime(upgradeDateTime, building, rankCost)) { |
|
Logger.error("Failed to set upgradeTime for building " + building.getObjectUUID()); |
|
return; |
|
} |
|
|
|
building.upgradeDateTime = upgradeDateTime; |
|
|
|
} |
|
|
|
// Method transfers ownership of all hirelings in a building |
|
|
|
public static void refreshHirelings(Building building) { |
|
|
|
if (building == null) |
|
return; |
|
|
|
Guild newGuild; |
|
|
|
if (building.getOwner() == null) |
|
newGuild = Guild.getErrantGuild(); |
|
else |
|
newGuild = building.getOwner().getGuild(); |
|
|
|
for (AbstractCharacter hireling : building.getHirelings().keySet()) { |
|
hireling.setGuild(newGuild); |
|
WorldGrid.updateObject(hireling); |
|
} |
|
|
|
} |
|
|
|
public static void cleanupHirelings(Building building) { |
|
|
|
// Early exit: Cannot have hirelings in a building |
|
// without a blueprint. |
|
|
|
if (building.getBlueprintUUID() == 0) |
|
return; |
|
|
|
// Remove all hirelings for destroyed buildings |
|
|
|
if (building.getRank() < 1) { |
|
|
|
for (AbstractCharacter slottedNPC : building.getHirelings().keySet()) { |
|
|
|
if (slottedNPC.getObjectType() == Enum.GameObjectType.NPC) |
|
((NPC)slottedNPC).remove(); |
|
else if (slottedNPC.getObjectType() == Enum.GameObjectType.Mob) |
|
NPCManager.removeMobileFromBuilding(((Mob) slottedNPC), building); |
|
} |
|
return; |
|
} |
|
|
|
// Delete hireling if building has deranked. |
|
for (AbstractCharacter hireling : building.getHirelings().keySet()) { |
|
|
|
NPC npc = null; |
|
Mob mob = null; |
|
|
|
if (hireling.getObjectType() == Enum.GameObjectType.NPC) |
|
npc = (NPC) hireling; |
|
else if (hireling.getObjectType() == Enum.GameObjectType.Mob) |
|
mob = (Mob) hireling; |
|
|
|
if (building.getHirelings().get(hireling) > building.getBlueprint().getSlotsForRank(building.getRank())) |
|
|
|
if (npc != null) { |
|
if (!npc.remove()) |
|
Logger.error("Failed to remove npc " + npc.getObjectUUID() |
|
+ "from Building " + building.getObjectUUID()); |
|
else |
|
building.getHirelings().remove(npc); |
|
} else if (mob != null) { |
|
if (!NPCManager.removeMobileFromBuilding(mob, building)) |
|
Logger.error("Failed to remove npc " + npc.getObjectUUID() |
|
+ "from Building " + building.getObjectUUID()); |
|
else |
|
building.getHirelings().remove(npc); |
|
} |
|
|
|
} |
|
|
|
refreshHirelings(building); |
|
} |
|
|
|
public static Building getBuilding(int id) { |
|
|
|
if (id == 0) |
|
return null; |
|
|
|
Building building; |
|
|
|
building = (Building) DbManager.getFromCache(Enum.GameObjectType.Building, id); |
|
|
|
if (building != null) |
|
return building; |
|
|
|
return DbManager.BuildingQueries.GET_BUILDINGBYUUID(id); |
|
|
|
} |
|
|
|
public static boolean PlayerCanControlNotOwner(Building building, PlayerCharacter player) { |
|
|
|
if (player == null) |
|
return false; |
|
|
|
if (building == null) |
|
return false; |
|
|
|
if (building.getOwner() == null) |
|
return false; |
|
|
|
//lets pass true if player is owner anyway. |
|
if (building.getOwner().equals(player)) |
|
return true; |
|
|
|
if (player.getGuild().isEmptyGuild()) |
|
return false; |
|
|
|
if (building.getGuild().isGuildLeader(player.getObjectUUID())) |
|
return true; |
|
|
|
if (!Guild.sameGuild(building.getGuild(), player.getGuild())) |
|
return false; |
|
|
|
return GuildStatusController.isGuildLeader(player.getGuildStatus()) != false || GuildStatusController.isInnerCouncil(player.getGuildStatus()) != false; |
|
} |
|
|
|
//This is mainly used for Rolling and gold sharing between building and warehouse. |
|
|
|
public static int GetWithdrawAmountForRolling(Building building, int cost) { |
|
|
|
//all funds are available to roll. |
|
|
|
if (cost <= GetAvailableGold(building)) |
|
return cost; |
|
|
|
// cost is more than available gold, return available gold |
|
|
|
return GetAvailableGold(building); |
|
|
|
} |
|
|
|
public static int GetAvailableGold(Building building) { |
|
|
|
if (building.getStrongboxValue() == 0) |
|
return 0; |
|
|
|
if (building.getStrongboxValue() < building.reserve) |
|
return 0; |
|
|
|
return building.getStrongboxValue() - building.reserve; |
|
} |
|
|
|
public static int GetOverdraft(Building building, int cost) { |
|
int availableGold = GetWithdrawAmountForRolling(building, cost); |
|
return cost - availableGold; |
|
} |
|
|
|
public static boolean IsPlayerHostile(Building building, PlayerCharacter player) { |
|
|
|
//Nation Members and Guild members are not hostile. |
|
// if (building.getGuild() != null){ |
|
// if (pc.getGuild() != null) |
|
// if (building.getGuild().getObjectUUID() == pc.getGuildUUID() |
|
// || pc.getGuild().getNation().getObjectUUID() == building.getGuild().getNation().getObjectUUID()) |
|
// return false; |
|
// } |
|
if (Guild.sameNationExcludeErrant(building.getGuild(), player.getGuild())) |
|
return false; |
|
|
|
if (!building.reverseKOS) { |
|
|
|
Condemned condemn = building.getCondemned().get(player.getObjectUUID()); |
|
|
|
if (condemn != null && condemn.isActive()) |
|
return true; |
|
|
|
if (player.getGuild() != null) { |
|
|
|
Condemned guildCondemn = building.getCondemned().get(player.getGuild().getObjectUUID()); |
|
|
|
if (guildCondemn != null && guildCondemn.isActive()) |
|
return true; |
|
|
|
Condemned nationCondemn = building.getCondemned().get(player.getGuild().getNation().getObjectUUID()); |
|
return nationCondemn != null && nationCondemn.isActive() && nationCondemn.getFriendType() == Condemned.NATION; |
|
} else { |
|
//TODO ADD ERRANT KOS CHECK |
|
} |
|
} else { |
|
|
|
Condemned condemn = building.getCondemned().get(player.getObjectUUID()); |
|
|
|
if (condemn != null && condemn.isActive()) |
|
return false; |
|
|
|
if (player.getGuild() != null) { |
|
|
|
Condemned guildCondemn = building.getCondemned().get(player.getGuild().getObjectUUID()); |
|
|
|
if (guildCondemn != null && guildCondemn.isActive()) |
|
return false; |
|
|
|
Condemned nationCondemn = building.getCondemned().get(player.getGuild().getNation().getObjectUUID()); |
|
return nationCondemn == null || !nationCondemn.isActive() || nationCondemn.getFriendType() != Condemned.NATION; |
|
} else { |
|
//TODO ADD ERRANT KOS CHECK |
|
} |
|
return true; |
|
} |
|
|
|
//When we get to here, This means The building was not reverse KOS |
|
//and passed the hostile test. |
|
|
|
return false; |
|
} |
|
|
|
public static final synchronized boolean addHirelingForWorld(Building building, PlayerCharacter contractOwner, Vector3fImmutable NpcLoc, Zone zone, Contract NpcID, int rank) { |
|
|
|
String pirateName = NPCManager.getPirateName(NpcID.getMobbaseID()); |
|
|
|
NPC npc = null; |
|
|
|
npc = NPC.createNPC(pirateName, NpcID.getObjectUUID(), NpcLoc, null, false, zone, (short)rank, building); |
|
|
|
if (npc == null) |
|
return false; |
|
|
|
npc.setBuilding(building); |
|
WorldGrid.addObject(npc, contractOwner); |
|
|
|
return true; |
|
|
|
} |
|
|
|
public static synchronized boolean addHireling(Building building, PlayerCharacter contractOwner, Zone zone, Contract contract, Item item) { |
|
|
|
int rank = 1; |
|
|
|
if (building.getBlueprintUUID() == 0) |
|
return false; |
|
|
|
if (building.getBlueprint().getMaxSlots() == building.getHirelings().size()) |
|
return false; |
|
|
|
String pirateName = NPCManager.getPirateName(contract.getMobbaseID()); |
|
|
|
if (item.getChargesRemaining() > 0) |
|
rank = item.getChargesRemaining() * 10; |
|
else rank = 10; |
|
|
|
Mob mob; |
|
NPC npc; |
|
|
|
if (NPC.ISWallArcher(contract)) { |
|
|
|
mob = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), true, zone, building, contract.getContractID(), pirateName, rank); |
|
|
|
if (mob == null) |
|
return false; |
|
|
|
WorldGrid.addObject(mob, contractOwner); |
|
|
|
return true; |
|
} |
|
|
|
if (NPC.ISGuardCaptain(contract.getContractID())) { |
|
|
|
mob = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), true, zone, building, contract.getContractID(), pirateName, rank); |
|
|
|
if (mob == null) |
|
return false; |
|
|
|
WorldGrid.addObject(mob, contractOwner); |
|
|
|
return true; |
|
} |
|
if (contract.getContractID() == 910) { |
|
//guard dog |
|
mob = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), true, zone, building, contract.getContractID(), pirateName, rank); |
|
|
|
if (mob == null) |
|
return false; |
|
|
|
WorldGrid.addObject(mob, contractOwner); |
|
|
|
return true; |
|
} |
|
npc = NPC.createNPC(pirateName, contract.getObjectUUID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), false, zone, (short) rank, building); |
|
|
|
if (npc == null) |
|
return false; |
|
|
|
WorldGrid.addObject(npc, contractOwner); |
|
|
|
return true; |
|
} |
|
|
|
public static boolean IsWallPiece(Building building) { |
|
|
|
if (building.getBlueprint() == null) |
|
return false; |
|
|
|
BuildingGroup buildingGroup = building.getBlueprint().getBuildingGroup(); |
|
|
|
switch (buildingGroup) { |
|
case WALLSTRAIGHT: |
|
case WALLCORNER: |
|
case SMALLGATE: |
|
case ARTYTOWER: |
|
case WALLSTRAIGHTTOWER: |
|
case WALLSTAIRS: |
|
return true; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
public static Building getBuildingFromCache(int id) { |
|
return (Building) DbManager.getFromCache(GameObjectType.Building, id); |
|
} |
|
|
|
public static boolean IsOwner(Building building, PlayerCharacter player) { |
|
if (building == null || player == null) |
|
return false; |
|
|
|
if (building.getOwner() == null) |
|
return false; |
|
|
|
|
|
return building.getOwner().getObjectUUID() == player.getObjectUUID(); |
|
|
|
} |
|
|
|
public static float GetMissingHealth(Building building) { |
|
return building.healthMax - building.getCurrentHitpoints(); |
|
} |
|
|
|
public static int GetRepairCost(Building building) { |
|
return (int) (GetMissingHealth(building) * .10f); |
|
} |
|
|
|
public static Regions GetRegion(Building building, float x, float y, float z) { |
|
if (building.getBounds() == null) |
|
return null; |
|
|
|
if (building.getBounds().getRegions() == null) |
|
return null; |
|
|
|
Regions currentRegion = null; |
|
for (Regions region : building.getBounds().getRegions()) { |
|
|
|
if (region.isPointInPolygon(new Vector3fImmutable(x, y, z))) { |
|
if (y > (region.highLerp.y - 5)) |
|
currentRegion = region; |
|
} |
|
} |
|
return currentRegion; |
|
} |
|
|
|
public static Regions GetRegion(Building building, int room, int level, float x, float z) { |
|
if (building.getBounds() == null) |
|
return null; |
|
|
|
if (building.getBounds().getRegions() == null) |
|
return null; |
|
|
|
for (Regions region : building.getBounds().getRegions()) { |
|
|
|
if (region.getLevel() != level) |
|
continue; |
|
if (region.getRoom() != room) |
|
continue; |
|
|
|
if (region.isPointInPolygon(new Vector3fImmutable(x, 0, z))) { |
|
return region; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public static Vector3fImmutable GetBindLocationForBuilding(Building building){ |
|
|
|
Vector3fImmutable bindLoc = null; |
|
|
|
if (building == null) |
|
return Enum.Ruins.getRandomRuin().getLocation(); |
|
|
|
|
|
bindLoc = building.getLoc(); |
|
|
|
|
|
float radius = Bounds.meshBoundsCache.get(building.getMeshUUID()).radius; |
|
if ( building.getRank() == 8){ |
|
bindLoc = building.getStuckLocation(); |
|
if (bindLoc != null) |
|
return bindLoc; |
|
} |
|
|
|
float x = bindLoc.getX(); |
|
float z = bindLoc.getZ(); |
|
float offset = ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * radius; |
|
int direction = ThreadLocalRandom.current().nextInt(4); |
|
|
|
switch (direction) { |
|
case 0: |
|
x += radius; |
|
z += offset; |
|
break; |
|
case 1: |
|
x += offset; |
|
z -= radius; |
|
break; |
|
case 2: |
|
x -= radius; |
|
z += offset; |
|
break; |
|
case 3: |
|
x += offset; |
|
z += radius; |
|
break; |
|
} |
|
bindLoc = new Vector3fImmutable(x, bindLoc.getY(), z); |
|
|
|
return bindLoc; |
|
|
|
|
|
|
|
} |
|
|
|
}
|
|
|