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.

792 lines
25 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.InterestManager;
import engine.InterestManagement.WorldGrid;
import engine.job.JobContainer;
import engine.job.JobScheduler;
import engine.jobs.UpgradeBuildingJob;
import engine.math.Bounds;
import engine.math.Vector3fImmutable;
import engine.net.client.ClientConnection;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.ManageCityAssetsMsg;
import engine.net.client.msg.PlaceAssetMsg;
import engine.objects.*;
import engine.server.MBServerStatics;
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 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;
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, 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 = 1;
if (building.getBlueprintUUID() == 0)
return false;
if (building.getBlueprint().getSlotsForRank(building.getRank()) == building.getHirelings().size()) {
Logger.error("failed at addHireling with contract: " + contract.getContractID());
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(), zone, building, contract, pirateName, rank);
if (mob == null)
return false;
mob.setLoc(mob.getLoc());
return true;
}
if (NPC.ISGuardCaptain(contract.getContractID())) {
mob = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), zone, building, contract, pirateName, rank);
if (mob == null)
return false;
mob.setLoc(mob.getLoc());
return true;
}
1 year ago
if (contract.getContractID() == 910) {
1 year ago
//guard dog
mob = Mob.createMob(contract.getMobbaseID(), Vector3fImmutable.ZERO, contractOwner.getGuild(), zone, building, contract, pirateName, rank);
1 year ago
if (mob == null)
return false;
mob.setLoc(mob.getLoc());
1 year ago
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 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;
}
public static void processRedeedNPC(NPC npc, Building building, ClientConnection origin) {
// Member variable declaration
PlayerCharacter player;
Contract contract;
CharacterItemManager itemMan;
ItemBase itemBase;
Item item;
npc.lock.writeLock().lock();
try {
if (building == null)
return;
player = SessionManager.getPlayerCharacter(origin);
itemMan = player.getCharItemManager();
contract = npc.getContract();
if (!player.getCharItemManager().hasRoomInventory((short) 1)) {
ErrorPopupMsg.sendErrorPopup(player, 21);
return;
}
if (!building.getHirelings().containsKey(npc))
return;
if (!npc.remove()) {
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return;
}
building.getHirelings().remove(npc);
itemBase = ItemBase.getItemBase(contract.getContractID());
if (itemBase == null) {
Logger.error("Could not find Contract for npc: " + npc.getObjectUUID());
return;
}
boolean itemWorked = false;
item = new Item(itemBase, player.getObjectUUID(), Enum.OwnerType.PlayerCharacter, (byte) ((byte) npc.getRank() - 1), (byte) ((byte) npc.getRank() - 1),
(short) 1, (short) 1, true, false, Enum.ItemContainerType.INVENTORY, (byte) 0,
new ArrayList<>(), "");
item.setNumOfItems(1);
item.containerType = Enum.ItemContainerType.INVENTORY;
try {
item = DbManager.ItemQueries.ADD_ITEM(item);
itemWorked = true;
} catch (Exception e) {
Logger.error(e);
}
if (itemWorked) {
itemMan.addItemToInventory(item);
itemMan.updateInventory();
}
ManageCityAssetsMsg mca = new ManageCityAssetsMsg();
mca.actionType = NPC.SVR_CLOSE_WINDOW;
mca.setTargetType(building.getObjectType().ordinal());
mca.setTargetID(building.getObjectUUID());
origin.sendMsg(mca);
} catch (Exception e) {
Logger.error(e);
} finally {
npc.lock.writeLock().unlock();
}
}
}