Files
Server/src/engine/objects/City.java
T

1428 lines
48 KiB
Java
Raw Normal View History

2022-04-30 09:41:17 -04:00
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.objects;
import engine.InterestManagement.RealmMap;
import engine.InterestManagement.WorldGrid;
import engine.db.archive.CityRecord;
import engine.db.archive.DataWarehouse;
import engine.gameManager.*;
import engine.math.Bounds;
import engine.math.FastMath;
import engine.math.Vector2f;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
import engine.mbEnums.*;
2022-04-30 09:41:17 -04:00
import engine.net.ByteBufferWriter;
import engine.net.Dispatch;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.TaxResourcesMsg;
2024-03-29 06:14:19 -04:00
import engine.net.client.msg.ViewResourcesMsg;
2022-04-30 09:41:17 -04:00
import engine.powers.EffectsBase;
import engine.server.MBServerStatics;
2023-06-07 13:37:43 -04:00
import engine.server.world.WorldServer;
2022-04-30 09:41:17 -04:00
import engine.workthreads.DestroyCityThread;
import engine.workthreads.TransferCityThread;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class City extends AbstractWorldObject {
2023-07-15 09:23:48 -04:00
public static long lastCityUpdate = 0;
public final HashSet<Integer> _playerMemory = new HashSet<>();
public java.time.LocalDateTime established;
public boolean hasBeenTransfered = false;
public LocalDateTime realmTaxDate;
public ReentrantReadWriteLock transactionLock = new ReentrantReadWriteLock();
public volatile boolean protectionEnforced = true;
public ArrayList<Building> cityBarracks;
public ArrayList<Integer> cityOutlaws = new ArrayList<>();
2023-10-17 16:32:36 -04:00
public Zone parentZone;
public int parentZoneUUID;
2023-07-15 09:23:48 -04:00
private String cityName;
private String motto;
private String description;
2024-05-17 19:41:26 -05:00
public int isNoobIsle; //1: noob, 0: not noob: -1: not noob, no teleport
2023-07-15 09:23:48 -04:00
private int population = 0;
private int siegesWithstood = 0;
private int radiusType;
private float bindRadius;
private float bindX;
private float bindZ;
private byte isNpc; //aka Safehold
private byte isCapital = 0;
private byte isSafeHold;
private boolean forceRename = false;
private boolean noTeleport = false; //used by npc cities
private boolean noRepledge = false; //used by npc cities
private int treeOfLifeID;
private Vector3fImmutable location = Vector3fImmutable.ZERO;
// Players who have entered the city (used for adding and removing affects)
private Vector3fImmutable bindLoc;
private boolean open = false;
private String hash;
2024-03-17 09:01:35 -04:00
public Warehouse warehouse;
2024-04-27 11:32:05 -04:00
public Realm realm;
2023-07-15 09:23:48 -04:00
/**
* ResultSet Constructor
*/
public City(ResultSet rs) throws SQLException {
super(rs);
try {
2023-10-17 16:32:36 -04:00
this.parentZoneUUID = rs.getInt("parent");
2023-07-15 09:23:48 -04:00
this.cityName = rs.getString("name");
this.motto = rs.getString("motto");
this.isNpc = rs.getByte("isNpc");
this.isSafeHold = (byte) ((this.isNpc == 1) ? 1 : 0);
this.description = ""; // TODO Implement this!
this.isNoobIsle = rs.getByte("isNoobIsle"); // Noob
this.gridObjectType = GridObjectType.STATIC;
// Island
// City(00000001),
// Otherwise(FFFFFFFF)
this.population = rs.getInt("population");
this.siegesWithstood = rs.getInt("siegesWithstood");
java.sql.Timestamp establishedTimeStamp = rs.getTimestamp("established");
if (establishedTimeStamp != null)
this.established = java.time.LocalDateTime.ofInstant(establishedTimeStamp.toInstant(), ZoneId.systemDefault());
this.location = Vector3fImmutable.ZERO;
2023-07-15 09:23:48 -04:00
java.sql.Timestamp realmTaxTimeStamp = rs.getTimestamp("realmTaxDate");
if (realmTaxTimeStamp != null)
this.realmTaxDate = realmTaxTimeStamp.toLocalDateTime();
if (this.realmTaxDate == null)
this.realmTaxDate = LocalDateTime.now();
this.treeOfLifeID = rs.getInt("treeOfLifeUUID");
this.bindX = rs.getFloat("bindX");
this.bindZ = rs.getFloat("bindZ");
2024-06-19 15:05:01 -04:00
this.bindLoc = new Vector3fImmutable(this.location.getX() + this.bindX, this.location.getY(), this.location.getZ() + this.bindZ);
2023-07-15 09:23:48 -04:00
this.radiusType = rs.getInt("radiusType");
2023-10-17 16:32:36 -04:00
2023-07-15 09:23:48 -04:00
float bindradiustemp = rs.getFloat("bindRadius");
2023-10-17 16:32:36 -04:00
2023-07-15 09:23:48 -04:00
if (bindradiustemp > 2)
bindradiustemp -= 2;
this.bindRadius = bindradiustemp;
this.forceRename = rs.getInt("forceRename") == 1;
this.open = rs.getInt("open") == 1;
this.hash = rs.getString("hash");
} catch (Exception e) {
Logger.error(e);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/*
* Utils
*/
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public static void _serializeForClientMsg(City city, ByteBufferWriter writer) {
City.serializeForClientMsg(city, writer);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public static void serializeForClientMsg(City city, ByteBufferWriter writer) {
AbstractCharacter guildRuler;
Guild rulingGuild;
Guild rulingNation;
java.time.LocalDateTime dateTime1900;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Cities aren't a city without a TOL. Time to early exit.
// No need to spam the log here as non-existant TOL's are indicated
// during bootstrap routines.
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (city.getTOL() == null) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Logger.error("NULL TOL FOR " + city.cityName);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Assign city owner
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (city.getTOL() != null)
guildRuler = city.getTOL().getOwner();
else
guildRuler = null;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// If is an errant tree, use errant guild for serialization.
// otherwise we serialize the soverign guild
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (guildRuler == null)
rulingGuild = Guild.getErrantGuild();
else
rulingGuild = guildRuler.getGuild();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
rulingNation = rulingGuild.getNation();
2022-04-30 09:41:17 -04:00
// Begin Serializing sovereign guild data
2023-07-15 09:23:48 -04:00
writer.putInt(city.getObjectType().ordinal());
writer.putInt(city.getObjectUUID());
writer.putString(city.cityName);
writer.putInt(rulingGuild.getObjectType().ordinal());
writer.putInt(rulingGuild.getObjectUUID());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putString(rulingGuild.getName());
writer.putString(city.motto);
writer.putString(rulingGuild.getLeadershipType());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Serialize guild ruler's name
// If tree is abandoned blank out the name
// to allow them a rename.
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (guildRuler == null)
writer.putString("");
else
writer.putString(guildRuler.getFirstName() + ' ' + guildRuler.getLastName());
2022-04-30 09:41:17 -04:00
2024-04-28 14:18:13 -04:00
writer.putInt(rulingGuild.charter.ordinal());
2023-07-15 09:23:48 -04:00
writer.putInt(0); // always 00000000
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.put(city.isSafeHold);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.put((byte) 1);
writer.put((byte) 1); // *** Refactor: What are these flags?
writer.put((byte) 1);
writer.put((byte) 1);
writer.put((byte) 1);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
GuildTag._serializeForDisplay(rulingGuild.getGuildTag(), writer);
GuildTag._serializeForDisplay(rulingNation.getGuildTag(), writer);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putInt(0);// TODO Implement description text
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.put((byte) 1);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (city.isCapital > 0)
writer.put((byte) 1);
else
writer.put((byte) 0);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.put((byte) 1);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Begin serializing nation guild info
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (rulingNation.isEmptyGuild()) {
writer.putInt(rulingGuild.getObjectType().ordinal());
writer.putInt(rulingGuild.getObjectUUID());
} else {
writer.putInt(rulingNation.getObjectType().ordinal());
writer.putInt(rulingNation.getObjectUUID());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Serialize nation name
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (rulingNation.isEmptyGuild())
writer.putString("None");
else
writer.putString(rulingNation.getName());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putInt(city.getTOL().getRank());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (city.isNoobIsle > 0)
writer.putInt(1);
else
writer.putInt(0xFFFFFFFF);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putInt(city.population);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (rulingNation.isEmptyGuild())
writer.putString(" ");
else
writer.putString(Guild.GetGL(rulingNation).getFirstName() + ' ' + Guild.GetGL(rulingNation).getLastName());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putLocalDateTime(city.established);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putFloat(city.location.x);
writer.putFloat(city.location.y);
writer.putFloat(city.location.z);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.putInt(city.siegesWithstood);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
writer.put((byte) 1);
writer.put((byte) 0);
writer.putInt(0x64);
writer.put((byte) 0);
writer.put((byte) 0);
writer.put((byte) 0);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public static Vector3fImmutable getBindLoc(int cityID) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
City city;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
city = City.getCity(cityID);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (city == null)
return mbEnums.Ruins.getRandomRuin().getLocation();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
return city.getBindLoc();
}
2022-04-30 09:41:17 -04:00
2024-06-19 16:56:06 -04:00
public static ArrayList<City> getCitiesToTeleportTo(PlayerCharacter playerCharacter, boolean repledge) {
2024-06-19 14:53:57 -04:00
2023-07-15 09:23:48 -04:00
ArrayList<City> cities = new ArrayList<>();
2022-04-30 09:41:17 -04:00
2024-06-19 14:53:57 -04:00
if (playerCharacter == null)
2023-07-15 09:23:48 -04:00
return cities;
2022-04-30 09:41:17 -04:00
ConcurrentHashMap<Integer, AbstractGameObject> worldCities = DbManager.getMap(mbEnums.GameObjectType.City);
2022-04-30 09:41:17 -04:00
2024-06-19 17:49:46 -04:00
//handle compiling of cities able to be teleported to for lore rule-set
2024-06-19 15:05:01 -04:00
2024-06-19 17:49:46 -04:00
for (AbstractGameObject ago : worldCities.values()) {
2024-06-19 14:53:57 -04:00
2024-06-19 17:49:46 -04:00
City city = (City) ago;
2024-06-19 14:53:57 -04:00
2024-06-19 17:49:46 -04:00
// Filter Player cities
2024-06-19 15:05:01 -04:00
2024-06-19 17:49:46 -04:00
if (city.parentZone == null)
continue;
2024-06-19 17:27:04 -04:00
2024-06-24 03:18:59 -04:00
//can't repledge to a guild you're already part of
if (repledge && city.getGuild().equals(playerCharacter.guild))
2024-06-22 19:22:13 -05:00
continue;
2024-06-19 17:27:04 -04:00
2024-06-22 19:22:13 -05:00
if (city.parentZone.guild_zone) {
2024-06-19 17:27:04 -04:00
2024-06-24 03:18:59 -04:00
//players can all port and repledge inside their own nation
2024-06-22 19:22:13 -05:00
if(city.getGuild().getNation().equals(playerCharacter.guild.getNation())){
cities.add(city);
continue;
2024-06-19 17:49:46 -04:00
}
2024-06-24 03:09:38 -04:00
2024-06-22 19:22:13 -05:00
if (city.isOpen() && city.getTOL().rank > 4) {
// Filter Lore cities
if (ConfigManager.MB_RULESET.getValue().equals("LORE")) {
2024-06-24 03:18:59 -04:00
if (repledge) {
if (!city.getGuild().charter.canJoin(playerCharacter))
2024-06-22 19:22:13 -05:00
continue;
2024-06-24 03:18:59 -04:00
} else if (!city.getGuild().charter.equals(playerCharacter.guild.charter))
2024-06-22 19:22:13 -05:00
continue;
}
2024-06-24 03:09:38 -04:00
2024-06-22 19:22:13 -05:00
Integer playerUUID = playerCharacter.objectUUID;
Integer guildUUID = playerCharacter.guildUUID;
Integer nationUUID = playerCharacter.guild.getNation().getObjectUUID();
boolean allowed = false;
2024-06-24 03:09:38 -04:00
2024-06-22 19:22:13 -05:00
if (city.getTOL().reverseKOS) {
2024-06-24 03:09:38 -04:00
2024-06-22 19:22:13 -05:00
//reverse KOS, specific values are allowed
2024-06-24 03:09:38 -04:00
if ((city.getTOL().getCondemned().containsKey(playerUUID) && city.getTOL().getCondemned().get(playerUUID).active) ||
(city.getTOL().getCondemned().containsKey(guildUUID) && city.getTOL().getCondemned().get(guildUUID).active) ||
(city.getTOL().getCondemned().containsKey(nationUUID) && city.getTOL().getCondemned().get(nationUUID).active))
2024-06-22 19:22:13 -05:00
allowed = true;
2024-06-24 03:09:38 -04:00
2024-06-22 19:22:13 -05:00
} else {
//not reverse KOS, everyone is allowed by default
2024-06-24 03:09:38 -04:00
2024-06-22 19:04:04 -05:00
allowed = true;
2024-06-24 03:09:38 -04:00
2024-06-22 19:22:13 -05:00
//specific values are not allowed
2024-06-24 03:09:38 -04:00
if ((city.getTOL().getCondemned().containsKey(playerUUID) && city.getTOL().getCondemned().get(playerUUID).active) ||
(city.getTOL().getCondemned().containsKey(guildUUID) && city.getTOL().getCondemned().get(guildUUID).active) ||
(city.getTOL().getCondemned().containsKey(nationUUID) && city.getTOL().getCondemned().get(nationUUID).active))
2024-06-22 19:22:13 -05:00
allowed = false;
2024-06-24 03:09:38 -04:00
2024-06-22 19:04:04 -05:00
}
2024-06-24 03:09:38 -04:00
if (allowed)
2024-06-22 19:22:13 -05:00
cities.add(city);
2024-06-19 17:49:46 -04:00
}
2024-06-22 19:22:13 -05:00
} else {
2022-04-30 09:41:17 -04:00
2024-06-22 19:22:13 -05:00
// Filter NPC cities
2024-06-19 17:27:04 -04:00
2024-06-22 19:22:13 -05:00
if (city.isNoobIsle == 1) {
2024-06-19 17:57:28 -04:00
2024-06-22 19:22:13 -05:00
if (playerCharacter.level < 20)
cities.add(city); // everyone can go to noob island if they are under level 20
2024-06-19 17:57:28 -04:00
2024-06-22 19:22:13 -05:00
continue;
}
2024-06-19 17:27:04 -04:00
2024-06-22 19:22:13 -05:00
// Players cannot teleport to perdition or bastion
2024-06-19 17:27:04 -04:00
2024-06-22 19:22:13 -05:00
if (city.cityName.equals("Perdition") || city.cityName.equals("Bastion"))
continue;
2024-06-19 17:27:04 -04:00
2024-06-22 19:22:13 -05:00
// These cities are available for anyone off noob island
2024-06-19 17:49:46 -04:00
2024-06-22 19:22:13 -05:00
if (playerCharacter.level >= 20 && (city.cityName.equals("Sea Dog's Rest") || city.cityName.equals("Khan'Ov Srekel") || city.cityName.equals("City of Khar Th'Sekt"))) {
cities.add(city);
continue;
}
2024-06-19 16:53:55 -04:00
2024-06-22 19:22:13 -05:00
// Add Lore cities
2024-06-19 17:49:46 -04:00
2024-06-22 19:22:13 -05:00
if (ConfigManager.MB_RULESET.getValue().equals("LORE")) {
2024-06-19 17:49:46 -04:00
2024-06-22 19:22:13 -05:00
if (repledge) {
if (city.getGuild().charter.canJoin(playerCharacter))
cities.add(city);
} else if (city.getGuild().charter.equals(playerCharacter.guild.charter))
2024-06-19 17:49:46 -04:00
cities.add(city);
2024-06-22 19:22:13 -05:00
}
2024-06-19 16:53:55 -04:00
2024-06-22 19:22:13 -05:00
}
2024-06-19 17:49:46 -04:00
}
2023-07-15 09:23:48 -04:00
return cities;
}
2022-04-30 09:41:17 -04:00
public static ArrayList<City> getCitiesToRepledgeTo(PlayerCharacter playerCharacter) {
2023-07-15 09:23:48 -04:00
ArrayList<City> cities = new ArrayList<>();
if (playerCharacter == null)
2023-07-15 09:23:48 -04:00
return cities;
Guild pcG = playerCharacter.getGuild();
2022-04-30 09:41:17 -04:00
ConcurrentHashMap<Integer, AbstractGameObject> worldCities = DbManager.getMap(mbEnums.GameObjectType.City);
if (ConfigManager.MB_RULESET.getValue().equals("LORE")) {
2024-05-14 19:28:19 -05:00
//handle compiling of cities able to be repledged to for lore rule-set
for (AbstractGameObject ago : worldCities.values()) {
City city = (City) ago;
2024-06-11 13:40:42 -04:00
if (city.cityName.equals("Perdition") || city.cityName.equals("Bastion"))
continue; // cannot repledge to perdition or bastion
2024-05-14 19:28:19 -05:00
if (city.isNpc == 1 && city.getGuild().charter.canJoin(playerCharacter)) {
cities.add(city); // anyone of the same charter can teleport to a safehold of that charter
continue;
} else if (city.isNoobIsle == 1 && playerCharacter.level <= 20) {
cities.add(city); // everyone can go to noob island if they are under level 20
continue;
2024-05-14 19:28:19 -05:00
} else if (city.isOpen() && city.getTOL().rank > 4 && city.getGuild().charter.canJoin(playerCharacter))
if (!city.getTOL().reverseKOS) {
2024-05-14 19:28:19 -05:00
cities.add(city);//can repledge to any open ToL that player can fit into charter
continue;
} else {
if (city.getTOL().getCondemned().contains(playerCharacter.objectUUID) && city.getTOL().getCondemned().get(playerCharacter.objectUUID).active) {
cities.add(city);//this player is allowed for the reverse KOS
continue;
}
if (city.getTOL().getCondemned().contains(playerCharacter.guildUUID) && city.getTOL().getCondemned().get(playerCharacter.guildUUID).active) {
cities.add(city);//this guild is allowed for the reverse KOS
continue;
}
if (city.getTOL().getCondemned().contains(playerCharacter.guild.getNation().getObjectUUID()) && city.getTOL().getCondemned().get(playerCharacter.guild.getNation().getObjectUUID()).active) {
cities.add(city);//this nation is allowed for the reverse KOS
continue;
}
}
2024-05-14 19:28:19 -05:00
if (city.getGuild().getNation().equals(playerCharacter.guild.getNation())) {
cities.add(city);//can always teleport inside your own nation
}
}
} else {
//add npc cities
2022-04-30 09:41:17 -04:00
for (AbstractGameObject ago : worldCities.values()) {
if (ago.getObjectType().equals(GameObjectType.City)) {
City city = (City) ago;
if (city.noRepledge)
continue;
if (city.parentZone != null && city.parentZone.guild_zone) {
2022-04-30 09:41:17 -04:00
//list Player cities
//open city, just list
if (playerCharacter.getAccount().status.equals(AccountStatus.ADMIN)) {
cities.add(city);
} else if (city.open && city.getTOL() != null && city.getTOL().getRank() > 4) {
2022-04-30 09:41:17 -04:00
if (!BuildingManager.IsPlayerHostile(city.getTOL(), playerCharacter))
cities.add(city); //verify nation or guild is same
} else if (Guild.sameNationExcludeErrant(city.getGuild(), pcG))
cities.add(city);
2022-04-30 09:41:17 -04:00
} else if (city.isNpc == 1) {
//list NPC cities
2022-04-30 09:41:17 -04:00
Guild guild = city.getGuild();
if (guild == null) {
if (city.isNpc == 1)
if (city.isNoobIsle == 1) {
if (playerCharacter.getLevel() < 21)
2024-04-28 11:34:03 -05:00
cities.add(city); //verify nation or guild is same
} else if (playerCharacter.getLevel() > 9)
2024-02-15 19:46:41 -06:00
cities.add(city); //verify nation or guild is same
} else if (playerCharacter.getLevel() >= guild.getRepledgeMin() && playerCharacter.getLevel() <= guild.getRepledgeMax()) {
2024-02-15 19:46:41 -06:00
cities.add(city); //verify nation or guild is same
}
2023-07-15 09:23:48 -04:00
}
}
}
}
return cities;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public static City getCity(int cityId) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (cityId == 0)
return null;
2022-04-30 09:41:17 -04:00
City city = (City) DbManager.getFromCache(mbEnums.GameObjectType.City, cityId);
2023-07-15 09:23:48 -04:00
if (city != null)
return city;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
return DbManager.CityQueries.GET_CITY(cityId);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public static City GetCityFromCache(int cityId) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (cityId == 0)
return null;
2022-04-30 09:41:17 -04:00
return (City) DbManager.getFromCache(mbEnums.GameObjectType.City, cityId);
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean renameCity(String cityName) {
2023-07-15 09:23:48 -04:00
if (!DbManager.CityQueries.renameCity(this, cityName))
return false;
2023-07-15 09:23:48 -04:00
if (!DbManager.CityQueries.updateforceRename(this, false))
return false;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.cityName = cityName;
this.forceRename = false;
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean updateTOL(Building tol) {
2023-07-15 09:23:48 -04:00
if (tol == null)
return false;
2023-07-15 09:23:48 -04:00
if (!DbManager.CityQueries.updateTOL(this, tol.getObjectUUID()))
return false;
2023-07-15 09:23:48 -04:00
this.treeOfLifeID = tol.getObjectUUID();
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean renameCityForNewPlant(String cityName) {
2023-07-15 09:23:48 -04:00
if (!DbManager.CityQueries.renameCity(this, cityName))
return false;
2023-07-15 09:23:48 -04:00
if (!DbManager.CityQueries.updateforceRename(this, true))
return false;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.cityName = cityName;
this.forceRename = true;
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public String getCityName() {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
return cityName;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public String getMotto() {
return motto;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public String getDescription() {
return description;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public Building getTOL() {
2023-07-15 09:23:48 -04:00
if (this.treeOfLifeID == 0)
return null;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
return BuildingManager.getBuildingFromCache(this.treeOfLifeID);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/**
* @param population the population to set
*/
public void setPopulation(int population) {
this.population = population;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public int getSiegesWithstood() {
return siegesWithstood;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/**
* @param siegesWithstood the siegesWithstood to set
*/
public void setSiegesWithstood(int siegesWithstood) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// early exit if setting to current value
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (this.siegesWithstood == siegesWithstood)
return;
2022-04-30 09:41:17 -04:00
2024-06-19 15:05:01 -04:00
if (DbManager.CityQueries.updateSiegesWithstood(this, siegesWithstood))
2023-07-15 09:23:48 -04:00
this.siegesWithstood = siegesWithstood;
else
Logger.error("Error when writing to database for cityUUID: " + this.getObjectUUID());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public float getAltitude() {
return this.location.y;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
@Override
public Vector3fImmutable getLoc() {
return this.location;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean isSafeHold() {
return (this.isSafeHold == (byte) 1);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public int getRank() {
return (this.getTOL() == null) ? 0 : this.getTOL().getRank();
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/*
* Serializing
*/
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public Bane getBane() {
return Bane.getBane(this.getObjectUUID());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public Zone getParent() {
return this.parentZone;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public AbstractCharacter getOwner() {
if (this.getTOL() == null)
return null;
int ownerID = this.getTOL().getOwnerUUID();
if (ownerID == 0)
return null;
if (this.isNpc == 1)
return NPC.getNPC(ownerID);
else
return PlayerCharacter.getPlayerCharacter(ownerID);
}
public Guild getGuild() {
if (this.getTOL() == null)
2024-06-19 14:43:31 -04:00
return Guild.getErrantGuild();
2023-07-15 09:23:48 -04:00
2024-06-19 14:43:31 -04:00
if (this.getTOL().getOwner() == null)
return Guild.getErrantGuild();
2024-06-19 14:43:31 -04:00
return this.getTOL().getOwner().getGuild();
2023-07-15 09:23:48 -04:00
}
public boolean openCity(boolean open) {
2023-07-15 09:23:48 -04:00
if (!DbManager.CityQueries.updateOpenCity(this, open))
return false;
2023-07-15 09:23:48 -04:00
this.open = open;
return true;
}
public Vector3fImmutable getBindLoc() {
2023-07-15 09:23:48 -04:00
Vector3fImmutable treeLoc = null;
if (this.getTOL() != null && this.getTOL().getRank() == 8)
treeLoc = this.getTOL().getStuckLocation();
if (treeLoc != null)
return treeLoc;
if (this.radiusType == 1 && this.bindRadius > 0f) {
2023-07-15 09:23:48 -04:00
//square radius
2023-07-15 09:23:48 -04:00
float x = this.bindLoc.getX();
float z = this.bindLoc.getZ();
float offset = ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * this.bindRadius;
int direction = ThreadLocalRandom.current().nextInt(4);
switch (direction) {
case 0:
x += this.bindRadius;
z += offset;
break;
case 1:
x += offset;
z -= this.bindRadius;
break;
case 2:
x -= this.bindRadius;
z += offset;
break;
case 3:
x += offset;
z += this.bindRadius;
break;
}
return new Vector3fImmutable(x, this.bindLoc.getY(), z);
} else if (this.radiusType == 2 && this.bindRadius > 0f) {
//circle radius
Vector3fImmutable dir = FastMath.randomVector2D();
return this.bindLoc.scaleAdd(this.bindRadius, dir);
} else if (this.radiusType == 3 && this.bindRadius > 0f) {
//random inside square
float x = this.bindLoc.getX();
x += ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * this.bindRadius;
float z = this.bindLoc.getZ();
z += ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * this.bindRadius;
return new Vector3fImmutable(x, this.bindLoc.getY(), z);
} else if (this.radiusType == 4 && this.bindRadius > 0f) {
//random inside circle
Vector3fImmutable dir = FastMath.randomVector2D();
return this.bindLoc.scaleAdd(ThreadLocalRandom.current().nextFloat() * this.bindRadius, dir);
} else
//spawn at bindLoc
//System.out.println("x: " + this.bindLoc.x + ", z: " + this.bindLoc.z);
return this.bindLoc;
}
public NPC getRuneMaster() {
NPC outNPC = null;
if (this.getTOL() == null)
return outNPC;
for (AbstractCharacter npc : getTOL().getHirelings().keySet()) {
if (npc.getObjectType() == GameObjectType.NPC)
2024-06-19 15:05:01 -04:00
if (((NPC) npc).getContract().isRuneMaster())
2023-07-15 09:23:48 -04:00
outNPC = (NPC) npc;
}
return outNPC;
}
public boolean isOpen() {
return open;
}
@Override
public void updateDatabase() {
// TODO Create update logic.
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
@Override
public void runAfterLoad() {
2023-10-17 16:32:36 -04:00
this.setObjectTypeMask(MBServerStatics.MASK_CITY);
// Set parent
this.parentZone = ZoneManager.getZoneByUUID(this.parentZoneUUID);
// If it's not a player city then must be an NPC city
if (!parentZone.guild_zone)
parentZone.isNPCCity = true;
// Set location for this city
this.location = new Vector3fImmutable(this.parentZone.absX, this.parentZone.absY, this.parentZone.absZ);
2024-06-19 15:05:01 -04:00
this.bindLoc = new Vector3fImmutable(this.location.x + this.bindX, this.location.y, this.location.z + this.bindZ);
2023-10-17 16:32:36 -04:00
// set city bounds
Bounds cityBounds = Bounds.borrow();
cityBounds.setBounds(new Vector2f(this.location.x + 64, this.location.z + 64), // location x and z are offset by 64 from the center of the city.
2024-06-19 15:05:01 -04:00
new Vector2f(mbEnums.CityBoundsType.GRID.halfExtents, mbEnums.CityBoundsType.GRID.halfExtents), 0.0f);
2023-10-17 16:32:36 -04:00
this.setBounds(cityBounds);
2022-04-30 09:41:17 -04:00
2023-10-17 17:07:52 -04:00
// Sanity check; no tol
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (BuildingManager.getBuilding(this.treeOfLifeID) == null)
Logger.info("City UID " + this.getObjectUUID() + " Failed to Load Tree of Life with ID " + this.treeOfLifeID);
2022-04-30 09:41:17 -04:00
2024-06-19 15:05:01 -04:00
if ((ConfigManager.serverType.equals(ServerType.WORLDSERVER)) && (this.isNpc == (byte) 0)) {
2022-04-30 09:41:17 -04:00
2024-04-27 11:32:05 -04:00
this.realm = RealmMap.getRealmAtLocation(this.getLoc());
2022-04-30 09:41:17 -04:00
2024-04-27 11:32:05 -04:00
if (realm != null)
realm.addCity(this.getObjectUUID());
2023-07-15 09:23:48 -04:00
else
2024-04-27 11:32:05 -04:00
Logger.error("Unable to find realm for city " + this.getObjectUUID());
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-10-17 17:07:52 -04:00
// Set city motto to current guild motto
2023-07-15 09:23:48 -04:00
if (this.getGuild() != null) {
this.motto = this.getGuild().getMotto();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Determine if this city is a nation capitol
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (this.getGuild().getGuildState() == GuildState.Nation)
for (Guild sub : this.getGuild().getSubGuildList()) {
2022-04-30 09:41:17 -04:00
2024-06-19 15:05:01 -04:00
if ((sub.getGuildState() == GuildState.Protectorate) || (sub.getGuildState() == GuildState.Province)) {
2023-07-15 09:23:48 -04:00
this.isCapital = 1;
2023-10-17 16:32:36 -04:00
break;
}
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
ArrayList<PlayerCharacter> guildList = Guild.GuildRoster(this.getGuild());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.population = guildList.size();
}
2022-04-30 09:41:17 -04:00
2023-10-17 16:32:36 -04:00
if (this.cityName.equals("Perdition") || this.cityName.equals("Bastion")) {
this.noTeleport = true;
this.noRepledge = true;
} else {
this.noTeleport = false;
this.noRepledge = false;
}
// Add city entry to data warehouse if newly created
if ((ConfigManager.serverType.equals(mbEnums.ServerType.WORLDSERVER)) && (this.getHash() == null)) {
2023-10-17 16:32:36 -04:00
this.setHash();
2024-06-19 15:05:01 -04:00
if (!DataWarehouse.recordExists(DataRecordType.CITY, this.getObjectUUID())) {
CityRecord cityRecord = CityRecord.borrow(this, mbEnums.RecordEventType.CREATE);
2023-10-17 16:32:36 -04:00
DataWarehouse.pushToWarehouse(cityRecord);
}
}
2023-10-19 08:20:28 -04:00
// Apply health bonus and special mesh for realm if applicable
if (this.getTOL().rank == 8) {
// Update mesh accordingly
this.getTOL().meshUUID = Realm.getRealmMesh(this);
// Apply realm capital health bonus.
// Do not apply bonus to banestones or TOL's. *** Refactor:
// Possibly only protected buildings? Needs some thought.
float missingHealth = 0;
if (this.health.get() != 0)
missingHealth = this.healthMax - this.health.get();
for (Building building : this.parentZone.zoneBuildingSet) {
2024-06-19 15:05:01 -04:00
if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() != BuildingGroup.BANESTONE && building.getBlueprint().getBuildingGroup() != BuildingGroup.TOL) {
2023-10-19 08:20:28 -04:00
building.healthMax += (building.healthMax * Realm.getRealmHealthMod(this));
if (this.health.get() != 0)
this.health.set(this.healthMax - missingHealth);
if (this.health.get() > this.healthMax)
this.health.set(this.healthMax);
}
}
}
2023-07-15 09:23:48 -04:00
// Banes are loaded for this city from the database at this point
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (this.getBane() == null)
return;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// if this city is baned, add the siege effect
2022-04-30 09:41:17 -04:00
2023-10-17 16:32:36 -04:00
this.getTOL().addEffectBit((1 << 16));
this.getBane().getStone().addEffectBit((1 << 19));
// Spawn city
this.setLoc(this.getLoc());
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void addCityEffect(EffectsBase effectBase, int rank) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
HashSet<AbstractWorldObject> currentPlayers;
PlayerCharacter player;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Add this new effect to the current city effect collection.
// so any new player to the grid will have all effects applied
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.addEffectNoTimer(Integer.toString(effectBase.getUUID()), effectBase, rank, false);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Any players currently in the zone will not be processed by the heartbeat
// if it's not the first effect toggled so we do it here manually
2022-04-30 09:41:17 -04:00
2023-09-20 15:43:01 -04:00
currentPlayers = WorldGrid.getObjectsInRangePartial(this.location, this.parentZone.bounds.getHalfExtents().x * 1.2f, MBServerStatics.MASK_PLAYER);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
for (AbstractWorldObject playerObject : currentPlayers) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (playerObject == null)
continue;
2023-07-15 09:23:48 -04:00
if (!this.isLocationWithinSiegeBounds(playerObject.getLoc()))
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player = (PlayerCharacter) playerObject;
player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, rank, MBServerStatics.FOURTYFIVE_SECONDS, true, this);
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void removeCityEffect(EffectsBase effectBase, int rank, boolean refreshEffect) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
PlayerCharacter player;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Remove the city effect from the ago's internal collection
2022-04-30 09:41:17 -04:00
this.getEffects().remove(Integer.toString(effectBase.getUUID()));
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Any players currently in the zone will not be processed by the heartbeat
// so we do it here manually
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
for (Integer playerID : this._playerMemory) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player = PlayerCharacter.getFromCache(playerID);
2023-07-15 09:23:48 -04:00
if (player == null)
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player.endEffectNoPower(Integer.toString(effectBase.getUUID()));
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Reapply effect with timeout?
2022-04-30 09:41:17 -04:00
2024-06-19 15:05:01 -04:00
if (refreshEffect)
2023-07-15 09:23:48 -04:00
player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, rank, MBServerStatics.FOURTYFIVE_SECONDS, false, this);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean isLocationOnCityGrid(Vector3fImmutable insideLoc) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Bounds newBounds = Bounds.borrow();
newBounds.setBounds(insideLoc);
boolean collided = Bounds.collide(this.getBounds(), newBounds, 0);
newBounds.release();
return collided;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean isLocationWithinSiegeBounds(Vector3fImmutable insideLoc) {
2022-04-30 09:41:17 -04:00
2023-09-17 12:23:06 -04:00
return insideLoc.isInsideCircle(this.getLoc(), CityBoundsType.ZONE.halfExtents);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public boolean isLocationOnCityZone(Vector3fImmutable insideLoc) {
2023-09-20 15:43:01 -04:00
return Bounds.collide(insideLoc, this.parentZone.bounds);
2023-07-15 09:23:48 -04:00
}
2022-06-05 17:27:40 -05:00
2023-07-15 09:23:48 -04:00
private void applyAllCityEffects(PlayerCharacter player) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Effect effect;
EffectsBase effectBase;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
try {
for (String cityEffect : this.getEffects().keySet()) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
effect = this.getEffects().get(cityEffect);
effectBase = effect.getEffectsBase();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (effectBase == null)
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, effect.getTrains(), MBServerStatics.FOURTYFIVE_SECONDS, true, this);
}
} catch (Exception e) {
Logger.error(e.getMessage());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
private void removeAllCityEffects(PlayerCharacter player, boolean force) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Effect effect;
EffectsBase effectBase;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
try {
for (String cityEffect : this.getEffects().keySet()) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
effect = this.getEffects().get(cityEffect);
effectBase = effect.getEffectsBase();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (player.getEffects().get(cityEffect) == null)
return;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// player.endEffectNoPower(cityEffect);
player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, effect.getTrains(), MBServerStatics.FOURTYFIVE_SECONDS, false, this);
}
} catch (Exception e) {
Logger.error(e.getMessage());
}
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/* All characters in city player memory but
* not the current memory have obviously
* left the city. Remove their affects.
*/
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void onEnter() {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
HashSet<AbstractWorldObject> currentPlayers;
HashSet<Integer> currentMemory;
PlayerCharacter player;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Gather current list of players within the zone bounds
2022-04-30 09:41:17 -04:00
2023-09-17 12:23:06 -04:00
currentPlayers = WorldGrid.getObjectsInRangePartial(this.location, CityBoundsType.ZONE.halfExtents, MBServerStatics.MASK_PLAYER);
2023-07-15 09:23:48 -04:00
currentMemory = new HashSet<>();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
for (AbstractWorldObject playerObject : currentPlayers) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (playerObject == null)
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player = (PlayerCharacter) playerObject;
currentMemory.add(player.getObjectUUID());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Player is already in our memory
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (_playerMemory.contains(player.getObjectUUID()))
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (!this.isLocationWithinSiegeBounds(player.getLoc()))
continue;
// Apply safehold affect to player if needed
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if ((this.isSafeHold == 1))
player.setSafeZone(true);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
//add spire effects.
if (this.getEffects().size() > 0)
this.applyAllCityEffects(player);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Add player to our city's memory
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
_playerMemory.add(player.getObjectUUID());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// ***For debugging
// Logger.info("PlayerMemory for ", this.getCityName() + ": " + _playerMemory.size());
}
try {
onExit(currentMemory);
} catch (Exception e) {
Logger.error(e.getMessage());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
private void onExit(HashSet<Integer> currentMemory) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
PlayerCharacter player;
int playerUUID = 0;
HashSet<Integer> toRemove = new HashSet<>();
Iterator<Integer> iter = _playerMemory.iterator();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
while (iter.hasNext()) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
playerUUID = iter.next();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player = PlayerCharacter.getFromCache(playerUUID);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (this.isLocationWithinSiegeBounds(player.getLoc()))
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Remove players safezone status if warranted
// they can assumed to be not on the citygrid at
// this point.
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
player.setSafeZone(false);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.removeAllCityEffects(player, false);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// We will remove this player after iteration is complete
// so store it in a temporary collection
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
toRemove.add(playerUUID);
// ***For debugging
// Logger.info("PlayerMemory for ", this.getCityName() + ": " + _playerMemory.size());
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Remove players from city memory
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
_playerMemory.removeAll(toRemove);
for (Integer removalUUID : toRemove) {
this.cityOutlaws.remove(removalUUID);
2023-07-15 09:23:48 -04:00
}
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public final void destroy() {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Thread destroyCityThread = new Thread(new DestroyCityThread(this));
2022-04-30 09:41:17 -04:00
2023-09-05 12:30:47 -04:00
destroyCityThread.setName("destroyCity:" + this.getName());
2023-07-15 09:23:48 -04:00
destroyCityThread.start();
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public final void transfer(AbstractCharacter newOwner) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Thread transferCityThread = new Thread(new TransferCityThread(this, newOwner));
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
transferCityThread.setName("TransferCity:" + this.getName());
transferCityThread.start();
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public final void claim(AbstractCharacter sourcePlayer) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Guild sourceNation;
Guild sourceGuild;
Zone cityZone;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
sourceGuild = sourcePlayer.getGuild();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (sourceGuild == null)
return;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
sourceNation = sourcePlayer.getGuild().getNation();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (sourceGuild.isEmptyGuild())
return;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
//cant claim tree with owned tree.
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (sourceGuild.getOwnedCity() != null)
return;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
cityZone = this.parentZone;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Can't claim a tree not in a player city zone
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Reset sieges withstood
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.setSiegesWithstood(0);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.hasBeenTransfered = true;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// If currently a sub of another guild, desub when
// claiming your new tree and set as Landed
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (!sourceNation.isEmptyGuild() && sourceNation != sourceGuild) {
if (!DbManager.GuildQueries.UPDATE_PARENT(sourceGuild.getObjectUUID(), WorldServer.worldUUID)) {
ChatManager.chatGuildError((PlayerCharacter) sourcePlayer, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
sourceNation.getSubGuildList().remove(sourceGuild);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (sourceNation.getSubGuildList().isEmpty())
sourceNation.downgradeGuildState();
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Link the mew guild with the tree
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (!DbManager.GuildQueries.SET_GUILD_OWNED_CITY(sourceGuild.getObjectUUID(), this.getObjectUUID())) {
ChatManager.chatGuildError((PlayerCharacter) sourcePlayer, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
sourceGuild.setCityUUID(this.getObjectUUID());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
sourceGuild.setNation(sourceGuild);
sourceGuild.setGuildState(GuildState.Sovereign);
GuildManager.updateAllGuildTags(sourceGuild);
GuildManager.updateAllGuildBinds(sourceGuild, this);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Build list of buildings within this parent zone
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
for (Building cityBuilding : cityZone.zoneBuildingSet) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Buildings without blueprints are unclaimable
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (cityBuilding.getBlueprintUUID() == 0)
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// All protection contracts are void upon transfer of a city
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// All protection contracts are void upon transfer of a city
//Dont forget to not Flip protection on Banestones and siege Equipment... Noob.
2024-06-19 15:05:01 -04:00
if (cityBuilding.getBlueprint() != null && !cityBuilding.getBlueprint().isSiegeEquip() && cityBuilding.getBlueprint().getBuildingGroup() != BuildingGroup.BANESTONE)
2023-07-15 09:23:48 -04:00
cityBuilding.setProtectionState(ProtectionState.NONE);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Transfer ownership of valid city assets
// these assets are autoprotected.
2022-04-30 09:41:17 -04:00
2024-06-19 15:05:01 -04:00
if ((cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) || (cityBuilding.getBlueprint().isWallPiece()) || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) {
PlayerCharacter guildLeader = PlayerCharacter.getPlayerCharacter(sourcePlayer.guild.getGuildLeaderUUID());
if(guildLeader != null)
cityBuilding.claim(guildLeader);
else
cityBuilding.claim(sourcePlayer);
2023-07-15 09:23:48 -04:00
cityBuilding.setProtectionState(ProtectionState.PROTECTED);
}
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.setForceRename(true);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Reset city timer for map update
City.lastCityUpdate = System.currentTimeMillis();
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public final boolean transferGuildLeader(PlayerCharacter sourcePlayer) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
Guild sourceGuild;
Zone cityZone;
sourceGuild = sourcePlayer.getGuild();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (sourceGuild == null)
return false;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (sourceGuild.isEmptyGuild())
return false;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
cityZone = this.parentZone;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
for (Building cityBuilding : cityZone.zoneBuildingSet) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Buildings without blueprints are unclaimable
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (cityBuilding.getBlueprintUUID() == 0)
continue;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// All protection contracts are void upon transfer of a city
//Dont forget to not Flip protection on Banestones and siege Equipment... Noob.
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Transfer ownership of valid city assets
// these assets are autoprotected.
2022-04-30 09:41:17 -04:00
2024-06-19 15:05:01 -04:00
if ((cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) || (cityBuilding.getBlueprint().isWallPiece()) || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
cityBuilding.claim(sourcePlayer);
cityBuilding.setProtectionState(ProtectionState.PROTECTED);
} else if (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.WAREHOUSE)
cityBuilding.claim(sourcePlayer);
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
this.setForceRename(true);
CityRecord cityRecord = CityRecord.borrow(this, mbEnums.RecordEventType.TRANSFER);
2023-07-15 09:23:48 -04:00
DataWarehouse.pushToWarehouse(cityRecord);
return true;
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
/**
* @return the forceRename
*/
public boolean isForceRename() {
return forceRename;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void setForceRename(boolean forceRename) {
if (!DbManager.CityQueries.updateforceRename(this, forceRename))
return;
this.forceRename = forceRename;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public String getHash() {
return hash;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void setHash(String hash) {
this.hash = hash;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
public void setHash() {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID());
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Write hash to player character table
DataWarehouse.writeHash(mbEnums.DataRecordType.CITY, this.getObjectUUID());
2023-07-15 09:23:48 -04:00
}
public boolean setRealmTaxDate(LocalDateTime realmTaxDate) {
if (!DbManager.CityQueries.updateRealmTaxDate(this, realmTaxDate))
return false;
this.realmTaxDate = realmTaxDate;
return true;
}
public synchronized boolean TaxWarehouse(TaxResourcesMsg msg, PlayerCharacter player) {
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
// Member variable declaration
Building building = BuildingManager.getBuildingFromCache(msg.getBuildingID());
Guild playerGuild = player.getGuild();
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (building == null) {
ErrorPopupMsg.sendErrorMsg(player, "Not a valid Building!");
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
City city = building.getCity();
2023-07-15 09:23:48 -04:00
if (city == null) {
ErrorPopupMsg.sendErrorMsg(player, "This building does not belong to a city.");
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (playerGuild == null || playerGuild.isEmptyGuild()) {
ErrorPopupMsg.sendErrorMsg(player, "You must belong to a guild to do that!");
return true;
}
2022-04-30 09:41:17 -04:00
2023-07-15 09:23:48 -04:00
if (playerGuild.getOwnedCity() == null) {
ErrorPopupMsg.sendErrorMsg(player, "Your Guild needs to own a city!");
return true;
}
if (playerGuild.getOwnedCity().getTOL() == null) {
ErrorPopupMsg.sendErrorMsg(player, "Cannot find Tree of Life for your city!");
return true;
}
if (playerGuild.getOwnedCity().getTOL().getRank() != 8) {
ErrorPopupMsg.sendErrorMsg(player, "Your City needs to Own a realm!");
return true;
}
2024-04-27 11:32:05 -04:00
if (RealmMap.getRealmIDAtLocation(playerGuild.getOwnedCity().getLoc()) == 0) {
2023-07-15 09:23:48 -04:00
ErrorPopupMsg.sendErrorMsg(player, "Cannot find realm for your city!");
return true;
}
2023-07-15 09:23:48 -04:00
Realm targetRealm = RealmMap.getRealmForCity(city);
if (targetRealm == null) {
ErrorPopupMsg.sendErrorMsg(player, "Cannot find realm for city you are attempting to tax!");
return true;
}
if (targetRealm.getRulingCity() == null) {
ErrorPopupMsg.sendErrorMsg(player, "Realm Does not have a ruling city!");
return true;
}
if (targetRealm.getRulingCity().getObjectUUID() != playerGuild.getOwnedCity().getObjectUUID()) {
ErrorPopupMsg.sendErrorMsg(player, "Your guild does not rule this realm!");
return true;
}
if (playerGuild.getOwnedCity().getObjectUUID() == city.getObjectUUID()) {
ErrorPopupMsg.sendErrorMsg(player, "You cannot tax your own city!");
return true;
}
if (!GuildStatusController.isTaxCollector(player.getGuildStatus())) {
ErrorPopupMsg.sendErrorMsg(player, "You Must be a tax Collector!");
return true;
}
if (this.realmTaxDate.isAfter(LocalDateTime.now()))
return true;
2023-07-15 09:23:48 -04:00
if (msg.getResources().size() == 0)
return true;
2024-03-17 09:01:35 -04:00
if (city.warehouse == null)
2023-07-15 09:23:48 -04:00
return true;
2024-03-17 09:01:35 -04:00
Warehouse ruledWarehouse = playerGuild.getOwnedCity().warehouse;
2023-07-15 09:23:48 -04:00
if (ruledWarehouse == null)
return true;
2024-03-15 10:47:35 -04:00
ArrayList<ResourceType> resources = new ArrayList<>();
2023-07-15 09:23:48 -04:00
float taxPercent = msg.getTaxPercent();
2023-07-15 09:23:48 -04:00
if (taxPercent > 20)
taxPercent = .20f;
2024-03-15 10:47:35 -04:00
for (int resourceHash : msg.getResources().keySet())
2024-06-11 13:09:40 -04:00
resources.add(ResourceType.templateHashLookup.get(resourceHash));
2024-02-28 16:31:09 -05:00
2024-03-15 10:47:35 -04:00
for (ResourceType resourceType : resources) {
2024-03-17 09:01:35 -04:00
if (Warehouse.isAboveCap(ruledWarehouse, resourceType, (int) (city.warehouse.resources.get(resourceType) * taxPercent))) {
2024-03-15 10:47:35 -04:00
ErrorPopupMsg.sendErrorMsg(player, "Your warehouse has enough " + resourceType.name() + " already!");
2023-07-15 09:23:48 -04:00
return true;
}
}
if (!city.setRealmTaxDate(LocalDateTime.now().plusDays(7))) {
ErrorPopupMsg.sendErrorMsg(player, "Failed to Update next Tax Date due to internal Error. City was not charged taxes this time.");
return false;
}
2023-07-15 09:23:48 -04:00
try {
2024-03-17 09:01:35 -04:00
Warehouse.transferResources(city.warehouse, player, msg, resources, taxPercent);
2023-07-15 09:23:48 -04:00
} catch (Exception e) {
Logger.info(e.getMessage());
}
// Member variable assignment
2024-03-29 06:14:19 -04:00
ViewResourcesMsg vrm = new ViewResourcesMsg(player);
2023-07-15 09:23:48 -04:00
vrm.setGuild(building.getGuild());
2024-03-17 11:36:26 -04:00
vrm.setWarehouseBuilding(BuildingManager.getBuildingFromCache(building.getCity().warehouse.building.getObjectUUID()));
2023-07-15 09:23:48 -04:00
vrm.configure();
Dispatch dispatch = Dispatch.borrow(player, vrm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
2023-07-15 09:23:48 -04:00
dispatch = Dispatch.borrow(player, msg);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
2023-07-15 09:23:48 -04:00
return true;
}
2024-04-27 11:32:05 -04:00
2022-04-30 09:41:17 -04:00
}