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.
646 lines
21 KiB
646 lines
21 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.objects; |
|
|
|
import engine.Enum; |
|
import engine.Enum.ProtectionState; |
|
import engine.Enum.SiegePhase; |
|
import engine.Enum.SiegeResult; |
|
import engine.InterestManagement.WorldGrid; |
|
import engine.db.archive.BaneRecord; |
|
import engine.db.archive.DataWarehouse; |
|
import engine.gameManager.BuildingManager; |
|
import engine.gameManager.ChatManager; |
|
import engine.gameManager.DbManager; |
|
import engine.gameManager.ZoneManager; |
|
import engine.job.JobScheduler; |
|
import engine.jobs.ActivateBaneJob; |
|
import engine.jobs.BaneDefaultTimeJob; |
|
import engine.math.Vector3fImmutable; |
|
import engine.net.DispatchMessage; |
|
import engine.net.client.ClientConnection; |
|
import engine.net.client.msg.PlaceAssetMsg; |
|
import engine.net.client.msg.chat.ChatSystemMsg; |
|
import engine.server.MBServerStatics; |
|
import org.joda.time.DateTime; |
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
import java.util.ArrayList; |
|
import java.util.Date; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
public final class Bane { |
|
|
|
public static ConcurrentHashMap<Integer, Bane> banes = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); |
|
private final int cityUUID; |
|
private final int stoneUUID; |
|
private int ownerUUID; |
|
private DateTime placementDate = null; |
|
private DateTime liveDate = null; |
|
private BaneDefaultTimeJob defaultTimeJob; |
|
|
|
// Internal cache for banes |
|
private ActivateBaneJob activateBaneJob; |
|
|
|
/** |
|
* ResultSet Constructor |
|
*/ |
|
public Bane(ResultSet rs) throws SQLException { |
|
|
|
Date sqlDateTime; |
|
ActivateBaneJob abtj; |
|
|
|
this.cityUUID = rs.getInt("cityUUID"); |
|
this.ownerUUID = rs.getInt("ownerUUID"); |
|
this.stoneUUID = rs.getInt("stoneUUID"); |
|
|
|
sqlDateTime = rs.getTimestamp("placementDate"); |
|
|
|
if (sqlDateTime != null) |
|
this.placementDate = new DateTime(sqlDateTime); |
|
|
|
sqlDateTime = rs.getTimestamp("liveDate"); |
|
|
|
if (sqlDateTime != null) |
|
this.liveDate = new DateTime(sqlDateTime); |
|
|
|
switch (this.getSiegePhase()) { |
|
|
|
case CHALLENGE: |
|
break; |
|
case WAR: |
|
|
|
// cancel old job if exists |
|
|
|
if (this.activateBaneJob != null) |
|
this.activateBaneJob.cancelJob(); |
|
|
|
abtj = new ActivateBaneJob(cityUUID); |
|
JobScheduler.getInstance().scheduleJob(abtj, this.liveDate.getMillis()); |
|
this.activateBaneJob = abtj; |
|
|
|
break; |
|
case STANDOFF: |
|
|
|
// cancel old job if exists |
|
|
|
if (this.activateBaneJob != null) |
|
this.activateBaneJob.cancelJob(); |
|
|
|
abtj = new ActivateBaneJob(cityUUID); |
|
JobScheduler.getInstance().scheduleJob(abtj, this.liveDate.getMillis()); |
|
this.activateBaneJob = abtj; |
|
|
|
break; |
|
} |
|
|
|
if (this.liveDate == null) |
|
setDefaultTime(); |
|
} |
|
|
|
public static boolean summonBanestone(PlayerCharacter player, ClientConnection origin, int rank) { |
|
|
|
Guild baningGuild; |
|
Zone cityZone; |
|
City targetCity; |
|
|
|
ArrayList<Bane> nationBanes; |
|
|
|
baningGuild = player.getGuild(); |
|
|
|
if (baningGuild.getNation().isEmptyGuild()) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 55, ""); // You must be in a Nation |
|
return false; |
|
} |
|
|
|
if (baningGuild.getNation().isNPCGuild()) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 72, ""); // Cannot be in an NPC nation |
|
return false; |
|
} |
|
|
|
if (GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 10, ""); // You must be a guild leader |
|
return false; |
|
} |
|
|
|
// Cannot place banestone underwater; |
|
|
|
if (ZoneManager.isLocUnderwater(player.getLoc())) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 6, ""); // Cannot place underwater |
|
return false; |
|
} |
|
|
|
// figure out which city zone we are standing on. |
|
|
|
targetCity = ZoneManager.getCityAtLocation(player.getLoc()); |
|
|
|
if (targetCity == null) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 59, ""); // No city to siege at this location! |
|
return false; |
|
} |
|
|
|
if (targetCity.isSafeHold()) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 15, ""); // Cannot place assets in peace zone |
|
return false; |
|
} |
|
|
|
if (targetCity.getRank() > rank) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 60, ""); // Bane rank is too low |
|
return false; |
|
} |
|
|
|
cityZone = targetCity.getParent(); |
|
|
|
// Cannot place assets on a dead tree |
|
|
|
if ((cityZone.guild_zone) && |
|
(City.getCity(cityZone.playerCityUUID).getTOL().getRank() == -1)) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Cannot bane a dead tree!"); |
|
return false; |
|
} |
|
|
|
if (baningGuild.getNation() == targetCity.getGuild().getNation()) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 20, ""); //Cannot bane yourself! |
|
return false; |
|
} |
|
|
|
if (targetCity.getTOL() == null) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 65, ""); // Cannot find tree to target |
|
return false; |
|
} |
|
|
|
if (targetCity.getBane() != null) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 23, ""); // Tree is already baned. |
|
return false; |
|
} |
|
|
|
if (getBaneByAttackerGuild(baningGuild) != null) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Your guild has already placed a bane!"); |
|
return false; |
|
} |
|
|
|
nationBanes = getBanesByNation(baningGuild.getNation()); |
|
|
|
// A nation can only have 3 concurrent banes |
|
|
|
if (nationBanes.size() == 3) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 64, ""); // Your nation is already at war and your limit has been reached |
|
return false; |
|
} |
|
|
|
if (targetCity.isLocationOnCityGrid(player.getLoc()) == true) { |
|
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Cannot place banestone on city grid."); |
|
return false; |
|
} |
|
|
|
Blueprint blueprint = Blueprint.getBlueprint(24300); // Banestone |
|
|
|
//Let's drop a banestone! |
|
|
|
Vector3fImmutable localLocation = ZoneManager.worldToLocal(player.getLoc(), cityZone); |
|
|
|
if (localLocation == null) { |
|
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
Logger.info("Failed to Convert World coordinates to local zone coordinates"); |
|
return false; |
|
} |
|
|
|
Building stone = DbManager.BuildingQueries.CREATE_BUILDING( |
|
cityZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getBlueprintUUID(), |
|
localLocation, 1.0f, blueprint.getMaxHealth(rank), ProtectionState.PROTECTED, 0, rank, |
|
null, blueprint.getBlueprintUUID(), 1, 0.0f); |
|
|
|
if (stone == null) { |
|
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
return false; |
|
} |
|
|
|
stone.addEffectBit((1 << 19)); |
|
stone.setMaxHitPoints(stone.getBlueprint().getMaxHealth(stone.getRank())); |
|
stone.setCurrentHitPoints(stone.getMaxHitPoints()); |
|
BuildingManager.setUpgradeDateTime(stone, null, 0); |
|
|
|
//Make the bane |
|
|
|
Bane bane = makeBane(player, targetCity, stone); |
|
|
|
if (bane == null) { |
|
//delete bane stone, failed to make bane object |
|
DbManager.BuildingQueries.DELETE_FROM_DATABASE(stone); |
|
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
return false; |
|
} |
|
|
|
WorldGrid.addObject(stone, player); |
|
|
|
//Add bane effect to TOL |
|
|
|
targetCity.getTOL().addEffectBit((1 << 16)); |
|
targetCity.getTOL().updateEffects(); |
|
|
|
Vector3fImmutable movePlayerOutsideStone = player.getLoc(); |
|
movePlayerOutsideStone = movePlayerOutsideStone.setX(movePlayerOutsideStone.x + 10); |
|
movePlayerOutsideStone = movePlayerOutsideStone.setZ(movePlayerOutsideStone.z + 10); |
|
player.teleport(movePlayerOutsideStone); |
|
|
|
// Notify players |
|
|
|
ChatSystemMsg chatMsg = new ChatSystemMsg(null, "[Bane Channel] " + targetCity.getCityName() + " has been baned by " + baningGuild.getName() + ". Standoff phase has begun!"); |
|
chatMsg.setMessageType(4); |
|
chatMsg.setChannel(Enum.ChatChannelType.SYSTEM.getChannelID()); |
|
|
|
DispatchMessage.dispatchMsgToAll(chatMsg); |
|
|
|
// Push this event to the DataWarehouse |
|
|
|
BaneRecord baneRecord = BaneRecord.borrow(bane, Enum.RecordEventType.PENDING); |
|
DataWarehouse.pushToWarehouse(baneRecord); |
|
|
|
return true; |
|
} |
|
|
|
public static Bane getBane(int cityUUID) { |
|
|
|
Bane outBane; |
|
|
|
// Check cache first |
|
|
|
outBane = banes.get(cityUUID); |
|
|
|
// Last resort attempt to load from database |
|
|
|
if (outBane == null) |
|
outBane = DbManager.BaneQueries.LOAD_BANE(cityUUID); |
|
else |
|
return outBane; |
|
|
|
// As we loaded from the db, store it in the internal cache |
|
|
|
if (outBane != null) |
|
banes.put(cityUUID, outBane); |
|
|
|
return outBane; |
|
} |
|
|
|
public static Bane getBaneByAttackerGuild(Guild guild) { |
|
|
|
|
|
if (guild == null || guild.isEmptyGuild()) |
|
return null; |
|
ArrayList<Bane> baneList; |
|
|
|
baneList = new ArrayList<>(banes.values()); |
|
|
|
for (Bane bane : baneList) { |
|
if (bane.getOwner().getGuild().equals(guild)) |
|
return bane; |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public static ArrayList<Bane> getBanesByNation(Guild guild) { |
|
|
|
ArrayList<Bane> baneList; |
|
ArrayList<Bane> returnList; |
|
|
|
baneList = new ArrayList<>(banes.values()); |
|
returnList = new ArrayList<>(); |
|
|
|
for (Bane bane : baneList) { |
|
if (bane.getOwner().getGuild().getNation().equals(guild)) |
|
returnList.add(bane); |
|
} |
|
|
|
return returnList; |
|
} |
|
|
|
public static void addBane(Bane bane) { |
|
|
|
Bane.banes.put(bane.cityUUID, bane); |
|
|
|
} |
|
|
|
public static Bane makeBane(PlayerCharacter owner, City city, Building stone) { |
|
|
|
Bane newBane; |
|
|
|
if (DbManager.BaneQueries.CREATE_BANE(city, owner, stone) == false) { |
|
Logger.error("Error writing to database"); |
|
return null; |
|
} |
|
|
|
newBane = DbManager.BaneQueries.LOAD_BANE(city.getObjectUUID()); |
|
|
|
return newBane; |
|
} |
|
|
|
//Call this to prematurely end a bane |
|
|
|
public SiegePhase getSiegePhase() { |
|
|
|
SiegePhase phase; |
|
|
|
if (this.liveDate == null) { |
|
phase = SiegePhase.CHALLENGE; //challenge |
|
return phase; |
|
} |
|
|
|
if (DateTime.now().isAfter(this.liveDate)) { |
|
phase = SiegePhase.WAR; //war |
|
return phase; |
|
} |
|
|
|
// If code reaches this point we are in standoff mode. |
|
phase = SiegePhase.STANDOFF; //standoff |
|
|
|
return phase; |
|
} |
|
|
|
// Cache access |
|
|
|
private void setDefaultTime() { |
|
|
|
DateTime timeToSetDefault = new DateTime(this.placementDate); |
|
timeToSetDefault = timeToSetDefault.plusDays(1); |
|
|
|
DateTime currentTime = DateTime.now(); |
|
DateTime defaultTime = new DateTime(this.placementDate); |
|
defaultTime = defaultTime.plusDays(2); |
|
defaultTime = defaultTime.hourOfDay().setCopy(22); |
|
defaultTime = defaultTime.minuteOfHour().setCopy(0); |
|
defaultTime = defaultTime.secondOfMinute().setCopy(0); |
|
|
|
if (currentTime.isAfter(timeToSetDefault)) |
|
this.setLiveDate(defaultTime); |
|
else { |
|
|
|
if (this.defaultTimeJob != null) |
|
this.defaultTimeJob.cancelJob(); |
|
|
|
BaneDefaultTimeJob bdtj = new BaneDefaultTimeJob(this); |
|
JobScheduler.getInstance().scheduleJob(bdtj, timeToSetDefault.getMillis()); |
|
this.defaultTimeJob = bdtj; |
|
} |
|
} |
|
|
|
//Returns a guild's bane |
|
|
|
/* |
|
* Getters |
|
*/ |
|
public City getCity() { |
|
return City.getCity(this.cityUUID); |
|
} |
|
|
|
public PlayerCharacter getOwner() { |
|
return (PlayerCharacter) DbManager.getObject(Enum.GameObjectType.PlayerCharacter, ownerUUID); |
|
} |
|
|
|
public boolean remove() { |
|
|
|
Building baneStone; |
|
|
|
baneStone = this.getStone(); |
|
|
|
if (baneStone == null) { |
|
Logger.debug("Removing bane without a stone."); |
|
return false; |
|
} |
|
|
|
// Reassert protection contracts |
|
|
|
this.getCity().protectionEnforced = true; |
|
|
|
// Remove visual effects on Bane and TOL. |
|
|
|
this.getStone().removeAllVisualEffects(); |
|
this.getStone().updateEffects(); |
|
|
|
this.getCity().getTOL().removeAllVisualEffects(); |
|
this.getCity().getTOL().updateEffects(); |
|
|
|
// Remove bane from the database |
|
|
|
if (DbManager.BaneQueries.REMOVE_BANE(this) == false) { |
|
Logger.error("Database call failed for city UUID: " + this.getCity().getObjectUUID()); |
|
return false; |
|
} |
|
|
|
// Remove bane from ingame cache |
|
|
|
Bane.banes.remove(cityUUID); |
|
|
|
// Delete stone from database |
|
|
|
if (DbManager.BuildingQueries.DELETE_FROM_DATABASE(baneStone) == false) { |
|
Logger.error("Database error when deleting stone object"); |
|
return false; |
|
} |
|
|
|
// Remove object from simulation |
|
|
|
baneStone.removeFromCache(); |
|
WorldGrid.RemoveWorldObject(baneStone); |
|
WorldGrid.removeObject(baneStone); |
|
return true; |
|
} |
|
|
|
public final DateTime getPlacementDate() { |
|
return placementDate; |
|
} |
|
|
|
public final boolean isAccepted() { |
|
|
|
return (this.getSiegePhase() != SiegePhase.CHALLENGE); |
|
} |
|
|
|
public final DateTime getLiveDate() { |
|
return liveDate; |
|
} |
|
|
|
public void setLiveDate(DateTime baneTime) { |
|
|
|
if (DbManager.BaneQueries.SET_BANE_TIME(baneTime, this.getCity().getObjectUUID())) { |
|
this.liveDate = new DateTime(baneTime); |
|
|
|
// Push event to warehouse |
|
|
|
BaneRecord.updateLiveDate(this, baneTime); |
|
|
|
ChatManager.chatGuildInfo(this.getOwner().getGuild(), "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); |
|
Guild attackerNation = this.getOwner().getGuild().getNation(); |
|
|
|
if (attackerNation != null) |
|
|
|
for (Guild subGuild : attackerNation.getSubGuildList()) { |
|
|
|
//Don't send the message twice. |
|
if (subGuild.equals(attackerNation)) |
|
continue; |
|
|
|
ChatManager.chatGuildInfo(subGuild, "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); |
|
} |
|
|
|
ChatManager.chatGuildInfo(this.getCity().getGuild(), "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); |
|
|
|
Guild defenderNation = this.getCity().getGuild().getNation(); |
|
|
|
if (defenderNation != null) { |
|
|
|
if (defenderNation != this.getCity().getGuild()) |
|
ChatManager.chatGuildInfo(defenderNation, "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); |
|
|
|
for (Guild subGuild : defenderNation.getSubGuildList()) { |
|
//Don't send the message twice. |
|
if (subGuild == this.getCity().getGuild()) |
|
continue; |
|
ChatManager.chatGuildInfo(subGuild, "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); |
|
} |
|
} |
|
|
|
if (activateBaneJob != null) |
|
this.activateBaneJob.cancelJob(); |
|
|
|
ActivateBaneJob abtj = new ActivateBaneJob(cityUUID); |
|
|
|
JobScheduler.getInstance().scheduleJob(abtj, this.liveDate.getMillis()); |
|
this.activateBaneJob = abtj; |
|
} else { |
|
Logger.debug("error with city " + this.getCity().getName()); |
|
ChatManager.chatGuildInfo(this.getOwner().getGuild(), "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
ChatManager.chatGuildInfo(this.getCity().getGuild(), "A Serious error has occurred. Please post details for to ensure transaction integrity"); |
|
} |
|
|
|
} |
|
|
|
public final void endBane(SiegeResult siegeResult) { |
|
|
|
boolean baneRemoved; |
|
|
|
// No matter what the outcome of a bane, we re-asset |
|
// protection contracts at this time. They don't quite |
|
// matter if the city falls, as they are invalidated. |
|
|
|
this.getCity().protectionEnforced = true; |
|
|
|
switch (siegeResult) { |
|
case DEFEND: |
|
|
|
// Push event to warehouse |
|
|
|
BaneRecord.updateResolution(this, Enum.RecordEventType.DEFEND); |
|
|
|
baneRemoved = remove(); |
|
|
|
if (baneRemoved) { |
|
|
|
// Update seieges withstood |
|
|
|
this.getCity().setSiegesWithstood(this.getCity().getSiegesWithstood() + 1); |
|
|
|
// Notify players |
|
|
|
ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel]" + this.getCity().getGuild().getName() + " has rallied against " + this.getOwner().getGuild().getName() + ". The siege on " + this.getCity().getCityName() + " has been broken!"); |
|
msg.setMessageType(4); |
|
msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); |
|
|
|
DispatchMessage.dispatchMsgToAll(msg); |
|
|
|
} |
|
|
|
break; |
|
case CAPTURE: |
|
|
|
// Push event to warehouse |
|
|
|
BaneRecord.updateResolution(this, Enum.RecordEventType.CAPTURE); |
|
|
|
baneRemoved = this.remove(); |
|
|
|
if (baneRemoved) { |
|
|
|
ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel]" + this.getOwner().getGuild().getName() + " have defeated " + this.getCity().getGuild().getName() + " and captured " + this.getCity().getCityName() + '!'); |
|
msg.setMessageType(4); |
|
msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); |
|
|
|
DispatchMessage.dispatchMsgToAll(msg); |
|
} |
|
break; |
|
case DESTROY: |
|
|
|
// Push event to warehouse |
|
|
|
BaneRecord.updateResolution(this, Enum.RecordEventType.DESTROY); |
|
|
|
baneRemoved = this.remove(); |
|
|
|
if (baneRemoved) { |
|
|
|
ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel]" + this.getOwner().getGuild().getName() + " have defeated " + this.getCity().getGuild().getName() + " and razed " + this.getCity().getCityName() + '!'); |
|
msg.setMessageType(4); |
|
msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); |
|
|
|
DispatchMessage.dispatchMsgToAll(msg); |
|
} |
|
break; |
|
} |
|
|
|
Zone cityZone = this.getCity().getParent(); |
|
|
|
if (cityZone == null) |
|
return; |
|
|
|
//UNPROTECT ALL SIEGE EQUIPMENT AFTER A BANE |
|
for (Building toUnprotect : cityZone.zoneBuildingSet) { |
|
if (toUnprotect.getBlueprint() != null && toUnprotect.getBlueprint().isSiegeEquip() && toUnprotect.assetIsProtected() == true) |
|
toUnprotect.setProtectionState(ProtectionState.NONE); |
|
} |
|
|
|
} |
|
|
|
public boolean isErrant() { |
|
|
|
boolean isErrant = true; |
|
|
|
if (this.getOwner() == null) |
|
return isErrant; |
|
|
|
|
|
if (this.getOwner().getGuild().isEmptyGuild() == true) |
|
return isErrant; |
|
|
|
if (this.getOwner().getGuild().getNation().isEmptyGuild() == true) |
|
return isErrant; |
|
|
|
// Bane passes validation |
|
|
|
isErrant = false; |
|
|
|
return isErrant; |
|
} |
|
|
|
/** |
|
* @return the stone |
|
*/ |
|
public Building getStone() { |
|
return BuildingManager.getBuilding(this.stoneUUID); |
|
} |
|
|
|
/** |
|
* @return the cityUUID |
|
*/ |
|
public int getCityUUID() { |
|
return cityUUID; |
|
} |
|
|
|
}
|
|
|