Dev commands, BuildingManager, ChatManager, ForgeManager, NPCManager, NPC, Warehouse, WorkOrder and ManageNPCMsg now call CObjectTemplate.lookup instead of ItemTemplate.templates.get. Forge production cost reads getItemResourceCosts(). Only the DB loader and linkCacheTemplates still use the legacy class (removed in Task 1.8). Removes the static_item_templates ParityCheck comparator. Final parity (offline p17.py, 7586 rows): every id has a cache template, 0 item_type differences, 0 item_resource_costs differences (84 ITEMs have costs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
872 lines
28 KiB
Java
872 lines
28 KiB
Java
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
// Magicbane Emulator Project © 2013 - 2026
|
|
// https://www.magicbane.com
|
|
|
|
|
|
package engine.gameManager;
|
|
|
|
import engine.InterestManagement.InterestManager;
|
|
import engine.InterestManagement.WorldGrid;
|
|
import engine.job.JobContainer;
|
|
import engine.job.JobScheduler;
|
|
import engine.jobs.UpgradeBuildingJob;
|
|
import engine.loot.WorkOrder;
|
|
import engine.math.Bounds;
|
|
import engine.math.Vector3fImmutable;
|
|
import engine.mbEnums;
|
|
import engine.mbEnums.BuildingGroup;
|
|
import engine.mbEnums.GameObjectType;
|
|
import engine.net.client.msg.ErrorPopupMsg;
|
|
import engine.objects.*;
|
|
import engine.parsers.objects.CObjectTemplate;
|
|
import engine.server.MBServerStatics;
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.ZoneId;
|
|
import java.util.ArrayList;
|
|
import java.util.EnumSet;
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
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 HashMap<Integer, ConcurrentHashMap<Integer, BuildingFriends>> _buildingFriends = new HashMap<>();
|
|
public static HashMap<Integer, ConcurrentHashMap<Integer, Condemned>> _buildingCondemned = new HashMap<>();
|
|
public static HashMap<Integer, ArrayList<Vector3fImmutable>> _buildingPatrolPoints = 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 int getLastAvailableSlot(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 = numOfSlots; i > 0; 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 boolean playerCanManage(PlayerCharacter player, Building building) {
|
|
|
|
if (player == null)
|
|
return false;
|
|
|
|
if (building == null)
|
|
return false;
|
|
|
|
//cannot access destroyed buildings
|
|
if (building.getRank() == -1)
|
|
return false;
|
|
|
|
//admin characters can always access buildings
|
|
if (player.isCSR())
|
|
return true;
|
|
|
|
//owner can always access their own building
|
|
if (IsOwner(building, player))
|
|
return true;
|
|
|
|
//check for default IC access if building belongs to same guild
|
|
if(player.guild.equals(building.getGuild())) {
|
|
if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() != null) {
|
|
switch (building.getBlueprint().getBuildingGroup()) {
|
|
case TOL:
|
|
case BARRACK:
|
|
case SPIRE:
|
|
case SHRINE:
|
|
case BANESTONE:
|
|
case MINE:
|
|
case WAREHOUSE:
|
|
case BULWARK:
|
|
case SIEGETENT:
|
|
if (GuildStatusController.isInnerCouncil(player.getGuildStatus()))
|
|
return true;
|
|
if (GuildStatusController.isGuildLeader(player.getGuildStatus()))
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//check against friends list entries if any present
|
|
if (building.getFriends() != null) {
|
|
|
|
//check individuals
|
|
if (building.getFriends().get(player.getObjectUUID()) != null)
|
|
return true;
|
|
|
|
if (building.getFriends().get(player.guild.objectUUID) != null) {
|
|
|
|
//check friend type for guild related access
|
|
switch (building.getFriends().get(player.guild.objectUUID).friendType) {
|
|
case 8: //full member
|
|
if (GuildStatusController.isFullMember(player.getGuildStatus()))
|
|
return true;
|
|
break;
|
|
case 9: //Inner Council
|
|
if (GuildStatusController.isInnerCouncil(player.getGuildStatus()))
|
|
return true;
|
|
if (GuildStatusController.isGuildLeader(player.getGuildStatus()))
|
|
return true;
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
//did not meet access grant criteria, deny access
|
|
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;
|
|
}
|
|
|
|
CObjectTemplate template = CObjectTemplate.lookup(1705032); // Elan Stone
|
|
|
|
if (!player.charItemManager.hasRoomInventory(template.getItemWt()))
|
|
return false;
|
|
|
|
if (!ItemManager.MakeItemForPlayer(template.getTemplateId(), player, amount))
|
|
return false;
|
|
|
|
shrine.setFavors(0);
|
|
break;
|
|
case WAREHOUSE:
|
|
|
|
Warehouse warehouse = Warehouse.destroyedWarehouses.get(building.getObjectUUID());
|
|
|
|
if (warehouse == null)
|
|
return false;
|
|
|
|
for (mbEnums.ResourceType resourceType : EnumSet.allOf(mbEnums.ResourceType.class)) {
|
|
|
|
CObjectTemplate resourceTemplate = CObjectTemplate.lookup(resourceType.templateID);
|
|
|
|
if (resourceTemplate == null)
|
|
continue;
|
|
|
|
if (!player.charItemManager.hasRoomInventory(resourceTemplate.getItemWt())) {
|
|
ChatManager.chatSystemInfo(player, "You can not carry any more of that item.");
|
|
return false;
|
|
}
|
|
|
|
if (warehouse.resources.get(resourceType) == null)
|
|
continue;
|
|
|
|
int resourceAmount = warehouse.resources.get(resourceType);
|
|
|
|
if (resourceAmount <= 0)
|
|
continue;
|
|
|
|
if (Warehouse.loot(warehouse, player, resourceType, resourceAmount, true)) {
|
|
ChatManager.chatInfoInfo(player, "You have looted " + resourceAmount + ' ' + resourceType.name());
|
|
}
|
|
|
|
}
|
|
|
|
if (Warehouse.isEmpty(warehouse))
|
|
Warehouse.destroyedWarehouses.remove(building.getObjectUUID());
|
|
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 removeHireling(Building building, AbstractCharacter hireling) {
|
|
|
|
if (hireling.getObjectType().equals(GameObjectType.Mob)) {
|
|
|
|
Mob guardCaptain = (Mob) hireling;
|
|
|
|
// Clear minions from database if a guard captain
|
|
|
|
if (guardCaptain.agentType.equals(mbEnums.AIAgentType.GUARDCAPTAIN))
|
|
DbManager.MobQueries.REMOVE_ALL_MINIONS(hireling.getObjectUUID());
|
|
}
|
|
|
|
// Clear minions from world
|
|
|
|
for (Integer minionUUID : hireling.minions) {
|
|
Mob minionMob = Mob.getMob(minionUUID);
|
|
DbManager.removeFromCache(minionMob);
|
|
WorldGrid.RemoveWorldObject(minionMob);
|
|
WorldGrid.unloadObject(minionMob);
|
|
|
|
if (minionMob.parentZone != null)
|
|
minionMob.parentZone.zoneMobSet.remove(minionMob);
|
|
}
|
|
|
|
// Clear all workorders for this hireling
|
|
if (hireling.getObjectType().equals(GameObjectType.NPC)) {
|
|
|
|
NPC hirelingNPC = (NPC) hireling;
|
|
|
|
if (ForgeManager.vendorWorkOrderLookup.get(hirelingNPC) != null)
|
|
|
|
for (WorkOrder workOrder : ForgeManager.vendorWorkOrderLookup.get(hirelingNPC)) {
|
|
workOrder.runCompleted.set(true);
|
|
workOrder.vendor = null;
|
|
|
|
// Remove any cooking items from collections
|
|
// to ensure we don't leak memory.
|
|
|
|
for (Item item : workOrder.cooking) {
|
|
DbManager.removeFromCache(item);
|
|
ForgeManager.itemWorkOrderLookup.remove(item);
|
|
}
|
|
|
|
DbManager.WarehouseQueries.DELETE_WORKORDER(workOrder);
|
|
}
|
|
|
|
// Finally remove the NPC from ForgeManager
|
|
|
|
ForgeManager.vendorWorkOrderLookup.remove(hirelingNPC);
|
|
}
|
|
|
|
// Remove hireling from building
|
|
|
|
building.getHirelings().remove(hireling);
|
|
|
|
// Remove from zone mob set
|
|
|
|
if (hireling.getObjectType().equals(GameObjectType.Mob)) {
|
|
|
|
Mob hirelingMob = (Mob) hireling;
|
|
|
|
if (hirelingMob.parentZone != null)
|
|
if (hirelingMob.parentZone.zoneMobSet.contains(hirelingMob))
|
|
hirelingMob.parentZone.zoneMobSet.remove(hireling);
|
|
}
|
|
|
|
if (hireling.getObjectType().equals(GameObjectType.NPC)) {
|
|
|
|
NPC hirelingNPC = (NPC) hireling;
|
|
|
|
if (hirelingNPC.getParentZone() != null)
|
|
if (hirelingNPC.getParentZone().zoneNPCSet.contains(hirelingNPC))
|
|
hirelingNPC.getParentZone().zoneNPCSet.remove(hireling);
|
|
|
|
}
|
|
|
|
// Unload hireling from world
|
|
|
|
DbManager.removeFromCache(hireling);
|
|
WorldGrid.RemoveWorldObject(hireling);
|
|
WorldGrid.removeObject(hireling);
|
|
|
|
// Delete hireling from database
|
|
|
|
if (hireling.getObjectType().equals(GameObjectType.Mob))
|
|
DbManager.MobQueries.DELETE_MOB((Mob) hireling);
|
|
else
|
|
DbManager.NPCQueries.DELETE_NPC((NPC) 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())
|
|
BuildingManager.removeHireling(building, slottedNPC);
|
|
return;
|
|
}
|
|
|
|
// Delete hireling if building has deranked.
|
|
|
|
for (AbstractCharacter hireling : building.getHirelings().keySet())
|
|
if (building.getHirelings().get(hireling) > building.getBlueprint().getSlotsForRank(building.getRank()))
|
|
BuildingManager.removeHireling(building, hireling);
|
|
|
|
refreshHirelings(building);
|
|
}
|
|
|
|
public static Building getBuilding(int id) {
|
|
|
|
if (id == 0)
|
|
return null;
|
|
|
|
Building building;
|
|
|
|
building = (Building) DbManager.getFromCache(mbEnums.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()) || GuildStatusController.isInnerCouncil(player.getGuildStatus());
|
|
}
|
|
|
|
public static boolean IsPlayerHostile(Building building, PlayerCharacter player) {
|
|
|
|
if (Guild.sameNationExcludeErrant(building.getGuild(), player.getGuild()))
|
|
return false;
|
|
|
|
if(building.enforceKOS) {
|
|
if (building.getCity() != null) {
|
|
Building TOL = building.getCity().getTOL();
|
|
if (TOL != null) {
|
|
building = TOL;
|
|
}
|
|
}
|
|
}
|
|
if (!building.reverseKOS) {
|
|
|
|
Condemned condemn = building.getCondemned().get(player.getObjectUUID());
|
|
|
|
if (condemn != null && condemn.active)
|
|
return true;
|
|
|
|
if (player.getGuild() != null) {
|
|
|
|
Condemned guildCondemn = building.getCondemned().get(player.getGuild().getObjectUUID());
|
|
|
|
if (guildCondemn != null && guildCondemn.active)
|
|
return true;
|
|
|
|
Condemned nationCondemn = building.getCondemned().get(player.getGuild().getNation().getObjectUUID());
|
|
return nationCondemn != null && nationCondemn.active && nationCondemn.friendType == Condemned.NATION;
|
|
} else {
|
|
//TODO ADD ERRANT KOS CHECK
|
|
}
|
|
} else {
|
|
|
|
Condemned condemn = building.getCondemned().get(player.getObjectUUID());
|
|
|
|
if (condemn != null && condemn.active)
|
|
return false;
|
|
|
|
if (player.getGuild() != null) {
|
|
|
|
Condemned guildCondemn = building.getCondemned().get(player.getGuild().getObjectUUID());
|
|
|
|
if (guildCondemn != null && guildCondemn.active)
|
|
return false;
|
|
|
|
Condemned nationCondemn = building.getCondemned().get(player.getGuild().getNation().getObjectUUID());
|
|
return nationCondemn == null || !nationCondemn.active || nationCondemn.friendType != 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;
|
|
|
|
npc = NPC.createNPC(pirateName, NpcID.getObjectUUID(), NpcLoc, building.getGuild(), zone, (short) rank, building);
|
|
|
|
if (npc == null)
|
|
return false;
|
|
|
|
npc.setObjectTypeMask(MBServerStatics.MASK_NPC);
|
|
npc.setLoc(npc.bindLoc);
|
|
InterestManager.setObjectDirty(npc);
|
|
return true;
|
|
|
|
}
|
|
|
|
public static synchronized boolean addHireling(Building building, PlayerCharacter contractOwner, Zone zone, Contract contract, Item item) {
|
|
|
|
int rank;
|
|
|
|
if (building.getBlueprintUUID() == 0)
|
|
return false;
|
|
|
|
if (building.getBlueprint().getMaxSlots() == building.getHirelings().size())
|
|
return false;
|
|
|
|
String pirateName = NPCManager.getPirateName(contract.getMobbaseID());
|
|
|
|
if ((byte) item.chargesRemaining > 0)
|
|
rank = (byte) item.chargesRemaining * 10;
|
|
else
|
|
rank = 10;
|
|
|
|
Mob mobile;
|
|
NPC npc;
|
|
|
|
if (NPC.ISWallArcher(contract)) {
|
|
|
|
mobile = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), zone, building, contract, pirateName, rank, mbEnums.AIAgentType.GUARDWALLARCHER);
|
|
|
|
if (mobile == null)
|
|
return false;
|
|
|
|
// Configure AI and write new mobile to disk
|
|
|
|
mobile.behaviourType = mbEnums.MobBehaviourType.GuardWallArcher;
|
|
mobile = DbManager.MobQueries.PERSIST(mobile);
|
|
|
|
// Spawn new mobile
|
|
|
|
mobile.setLoc(mobile.getLoc());
|
|
|
|
return true;
|
|
}
|
|
|
|
if (NPC.ISGuardCaptain(contract.getContractID())) {
|
|
|
|
mobile = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), zone, building, contract, pirateName, rank, mbEnums.AIAgentType.GUARDCAPTAIN);
|
|
|
|
if (mobile == null)
|
|
return false;
|
|
|
|
// Configure AI and write new mobile to disk
|
|
|
|
mobile.behaviourType = mbEnums.MobBehaviourType.GuardCaptain;
|
|
mobile = DbManager.MobQueries.PERSIST(mobile);
|
|
|
|
// Spawn new mobile
|
|
|
|
mobile.setLoc(mobile.getLoc());
|
|
|
|
return true;
|
|
}
|
|
|
|
if (contract.getContractID() == 910) {
|
|
|
|
//guard dog
|
|
|
|
mobile = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), zone, building, contract, pirateName, rank, mbEnums.AIAgentType.GUARDCAPTAIN);
|
|
|
|
if (mobile == null)
|
|
return false;
|
|
|
|
// Configure AI and write new mobile to disk
|
|
|
|
mobile.behaviourType = mbEnums.MobBehaviourType.GuardCaptain;
|
|
mobile = DbManager.MobQueries.PERSIST(mobile);
|
|
|
|
// Spawn new mobile
|
|
|
|
mobile.setLoc(mobile.getLoc());
|
|
|
|
return true;
|
|
}
|
|
|
|
npc = NPC.createNPC(pirateName, contract.getObjectUUID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), zone, (short) rank, building);
|
|
|
|
if (npc == null)
|
|
return false;
|
|
|
|
npc.setObjectTypeMask(MBServerStatics.MASK_NPC);
|
|
npc.setLoc(npc.bindLoc);
|
|
InterestManager.setObjectDirty(npc);
|
|
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 mbEnums.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;
|
|
|
|
|
|
}
|
|
|
|
public static void rebuildMine(Building mineBuilding) {
|
|
setRank(mineBuilding, 1);
|
|
mineBuilding.meshUUID = mineBuilding.getBlueprint().getMeshForRank(mineBuilding.rank);
|
|
|
|
// New rank mean new max hit points.
|
|
|
|
mineBuilding.healthMax = mineBuilding.getBlueprint().getMaxHealth(mineBuilding.rank);
|
|
mineBuilding.setCurrentHitPoints(mineBuilding.healthMax);
|
|
mineBuilding.getBounds().setBounds(mineBuilding);
|
|
}
|
|
|
|
public static void setRank(Building building, int rank) {
|
|
|
|
int newMeshUUID;
|
|
boolean success;
|
|
|
|
// If this building has no blueprint then set rank and exit immediatly.
|
|
|
|
if (building.blueprintUUID == 0 || building.getBlueprint() != null && building.getBlueprint().getBuildingGroup().equals(BuildingGroup.MINE)) {
|
|
building.rank = rank;
|
|
DbManager.BuildingQueries.CHANGE_RANK(building.getObjectUUID(), rank);
|
|
return;
|
|
}
|
|
|
|
// Delete any upgrade jobs before doing anything else. It won't quite work
|
|
// if in a few lines we happen to delete this building.
|
|
|
|
JobContainer jobContainer = building.getTimers().get("UPGRADE");
|
|
|
|
if (jobContainer != null) {
|
|
if (!JobScheduler.getInstance().cancelScheduledJob(jobContainer))
|
|
Logger.error("failed to cancel existing upgrade job.");
|
|
}
|
|
|
|
// Attempt to write to database or delete the building
|
|
// if we are destroying it.
|
|
|
|
if (rank == -1)
|
|
success = DbManager.BuildingQueries.DELETE_FROM_DATABASE(building);
|
|
else
|
|
success = DbManager.BuildingQueries.updateBuildingRank(building, rank);
|
|
|
|
if (!success) {
|
|
Logger.error("Error writing to database UUID: " + building.getObjectUUID());
|
|
return;
|
|
}
|
|
|
|
building.isDeranking.compareAndSet(false, true);
|
|
|
|
// Change the building's rank
|
|
|
|
building.rank = rank;
|
|
|
|
// New rank means new mesh
|
|
newMeshUUID = building.getBlueprint().getMeshForRank(building.rank);
|
|
|
|
if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) && (building.rank == 8))
|
|
newMeshUUID = Realm.getRealmMesh(building.getCity());
|
|
|
|
building.meshUUID = newMeshUUID;
|
|
|
|
// New rank mean new max hitpoints.
|
|
|
|
building.healthMax = building.getBlueprint().getMaxHealth(building.rank);
|
|
building.setCurrentHitPoints(building.healthMax);
|
|
|
|
if (building.getUpgradeDateTime() != null)
|
|
setUpgradeDateTime(building, null, 0);
|
|
|
|
// If we destroyed this building make sure to turn off
|
|
// protection
|
|
|
|
if (building.rank == -1)
|
|
building.protectionState = mbEnums.ProtectionState.NONE;
|
|
|
|
// update object to clients
|
|
|
|
building.refresh(true);
|
|
|
|
if (building.getBounds() != null)
|
|
building.getBounds().setBounds(building);
|
|
|
|
// Cleanup hirelings resulting from rank change
|
|
|
|
cleanupHirelings(building);
|
|
|
|
building.isDeranking.compareAndSet(true, false);
|
|
}
|
|
|
|
public static Building getBuildingAtLocation(Vector3fImmutable loc) {
|
|
|
|
for (AbstractWorldObject awo : WorldGrid.getObjectsInRangePartial(loc, 64, MBServerStatics.MASK_BUILDING)) {
|
|
Building building = (Building) awo;
|
|
|
|
if (building == null)
|
|
continue;
|
|
|
|
if (Bounds.collide(loc, building.getBounds()))
|
|
return building;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
}
|