package l2p.gameserver.model.entity.residence;
import javolution.util.FastMap;
import l2p.Config;
import l2p.common.ThreadPoolManager;
import l2p.database.DatabaseUtils;
import l2p.database.FiltredPreparedStatement;
import l2p.database.L2DatabaseFactory;
import l2p.database.ThreadConnection;
import l2p.gameserver.model.L2Clan;
import l2p.gameserver.model.L2Object;
import l2p.gameserver.model.L2Player;
import l2p.gameserver.model.L2Skill;
import l2p.gameserver.model.L2World;
import l2p.gameserver.model.L2Zone;
import l2p.gameserver.model.entity.siege.Siege;
import l2p.gameserver.model.instances.L2DoorInstance;
import l2p.gameserver.modules.data.DoorTable;
import l2p.gameserver.tables.ClanTable;
import l2p.util.GArray;
import l2p.util.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.sql.ResultSet;
import java.util.Calendar;
import java.util.logging.Logger;
public abstract class Residence
{
protected static Logger _log = Logger.getLogger(Residence.class.getName());
protected int _id = 0;
protected String _name = "";
/**
* Clan objectId
*/
protected int _ownerId = 0;
protected int _ownDate = 0;
protected L2Zone _zone;
protected ResidenceType _type = ResidenceType.None;
private GArray<L2DoorInstance> _doors = new GArray<L2DoorInstance>();
private GArray<ResidenceFunction> _functions = new GArray<ResidenceFunction>();
protected GArray<L2Skill> _skills = new GArray<L2Skill>();
public Residence(int id)
{
_id = id;
}
public void init()
{
loadData();
loadDoor();
preLoadFunctions();
loadFunctions();
rewardSkills();
}
public int getId()
{
return _id;
}
public String getName()
{
return _name;
}
public int getOwnerId()
{
return _ownerId;
}
public L2Clan getOwner()
{
return _ownerId > 0 ? ClanTable.getInstance().getClan(_ownerId) : null;
}
public L2Zone getZone()
{
return _zone;
}
protected abstract void loadData();
public abstract Siege getSiege();
public abstract int getSiegeDayOfWeek();
public abstract int getSiegeHourOfDay();
/**
* This method sets the residence owner; null here means give it back to NPC
*/
public abstract void changeOwner(L2Clan clan);
/**
* Unixtime в секундах
*/
public void setOwnDate(int val)
{
_ownDate = val;
}
/**
* Unixtime в секундах
*/
public int getOwnDate()
{
return _ownDate;
}
public abstract void saveOwnDate();
/**
* Возвращает дату последней осады на момент ее старта, unixtime в секундах
*/
public int getLastSiegeDate()
{
return 0;
}
/**
* Unixtime в секундах
*/
public void setLastSiegeDate(int time)
{
}
public GArray<L2DoorInstance> getDoors()
{
return _doors;
}
public L2DoorInstance getDoor(int doorId)
{
if(doorId <= 0)
{
return null;
}
for(int i = 0; i < getDoors().size(); i++)
{
L2DoorInstance door = getDoors().get(i);
if(door.getDoorId() == doorId)
{
return door;
}
}
return null;
}
/**
* Return true if object is inside the zone
*/
public boolean checkIfInZone(L2Object obj)
{
return checkIfInZone(obj.getX(), obj.getY());
}
/**
* Return true if object is inside the zone
*/
public boolean checkIfInZone(int x, int y)
{
return getZone().checkIfInZone(x, y);
}
/**
* Respawn all doors on residence grounds
*/
public void spawnDoor(boolean isDoorWeak)
{
for(int i = 0; i < getDoors().size(); i++)
{
L2DoorInstance door = getDoors().get(i);
if(door.getCurrentHp() <= 0) // Сломанные восстанавливаем
{
door.decayMe(); // Kill current if not killed already
door.spawnMe();
}
door.setCurrentHp(isDoorWeak ? door.getMaxHp() / 2 : door.getMaxHp(), true);
door.closeMe();
}
loadDoorUpgrade(); // Check for any upgrade the doors may have
}
/**
* Respawn all doors
*/
public void spawnDoor()
{
spawnDoor(false);
}
FastMap<Integer, Integer> doorUpgrades = new FastMap<Integer, Integer>().setShared(true);
// This method upgrade door
public void upgradeDoor(int doorId, int hp, boolean db)
{
L2DoorInstance door = getDoor(doorId);
if(door == null)
{
return;
}
if(door.getDoorId() == doorId)
{
door.setUpgradeHp(hp);
door.setCurrentHp(door.getMaxHp(), false);
door.broadcastStatusUpdate();
if(db)
{
saveDoorUpgrade(doorId, hp);
}
doorUpgrades.put(doorId, hp);
}
}
public Integer getDoorUpgrade(int doorId)
{
if(doorUpgrades.get(doorId) != null)
{
return doorUpgrades.get(doorId);
}
return 0;
}
// This method is used to begin removing all residence upgrades
public void removeUpgrade()
{
removeDoorUpgrade();
}
/**
* Move non clan members off siegeUnit area and to nearest town.
*/
public void banishForeigner(L2Player activeChar)
{
// Get all players
for(L2Player player : L2World.getAroundPlayers(activeChar))
{
// Skip if player is in clan
if(player.getClanId() == getOwnerId())
{
continue;
}
if(checkIfInZone(player))
{
player.teleToClosestTown();
}
}
}
public void closeDoor(L2Player activeChar, int doorId)
{
openCloseDoor(activeChar, doorId, false);
}
public void openDoor(L2Player activeChar, int doorId)
{
openCloseDoor(activeChar, doorId, true);
}
public void openCloseDoor(L2Player activeChar, int doorId, boolean open)
{
if((activeChar.getClanId() != getOwnerId() || getType() != ResidenceType.Castle && getSiege() != null && getSiege().isInProgress()) && !activeChar.isGM())
{
return;
}
L2DoorInstance door = getDoor(doorId);
if(door != null)
{
if(open)
{
door.openMe();
}
else
{
door.closeMe();
}
}
}
public void openCloseDoors(L2Player activeChar, boolean open)
{
if((activeChar.getClanId() != getOwnerId() || getType() != ResidenceType.Castle && getSiege() != null && getSiege().isInProgress()) && !activeChar.isGM())
{
return;
}
for(L2DoorInstance door : getDoors())
{
if(door != null)
{
if(open)
{
door.openMe();
}
else
{
door.closeMe();
}
}
}
}
// This method loads door data from database
private void loadDoor()
{
int id = 0;
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
ResultSet rset = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("SELECT id FROM siege_door WHERE unitId = ?");
statement.setInt(1, getId());
rset = statement.executeQuery();
while(rset.next())
{
id = rset.getInt("id");
L2DoorInstance door = DoorTable.getInstance().getDoor(rset.getInt("id"));
door.setSiegeUnit(this);
door.decayMe();
door.spawnMe();
_doors.add(door);
}
}
catch(Exception e)
{
_log.warning("Exception: loadDoor(), id = " + id + ", error: " + e);
e.printStackTrace();
}
finally
{
DatabaseUtils.closeDatabaseCSR(con, statement, rset);
}
}
// This method loads door upgrade data from database
private void loadDoorUpgrade()
{
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
ResultSet rset = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("SELECT * FROM siege_doorupgrade WHERE doorId IN (SELECT id FROM siege_door WHERE unitId = ?)");
statement.setInt(1, getId());
rset = statement.executeQuery();
while(rset.next())
{
upgradeDoor(rset.getInt("doorId"), rset.getInt("hp"), false);
}
}
catch(Exception e)
{
_log.warning("Exception: loadDoorUpgrade(): " + e.getMessage());
e.printStackTrace();
}
finally
{
DatabaseUtils.closeDatabaseCSR(con, statement, rset);
}
}
private void removeDoorUpgrade()
{
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("DELETE FROM siege_doorupgrade WHERE doorId IN (SELECT id FROM siege_door WHERE unitId=?)");
statement.setInt(1, getId());
statement.execute();
}
catch(Exception e)
{
_log.warning("Exception: removeDoorUpgrade(): " + e);
}
finally
{
DatabaseUtils.closeDatabaseCS(con, statement);
}
}
private void saveDoorUpgrade(int doorId, int hp)
{
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("REPLACE INTO siege_doorupgrade (doorId, hp) VALUES (?,?)");
statement.setInt(1, doorId);
statement.setInt(2, hp);
statement.execute();
}
catch(Exception e)
{
_log.warning("Exception: saveDoorUpgrade(int doorId, int hp): " + e);
}
finally
{
DatabaseUtils.closeDatabaseCS(con, statement);
}
}
/**
* Выдает клану-владельцу скилы резиденции
*/
protected void rewardSkills()
{
if(getOwner() != null)
{
for(L2Skill skill : _skills)
{
getOwner().addNewSkill(skill, false);
}
if(getType() == ResidenceType.Castle)
{
for(L2Skill skill : ((Castle) this).getTerritorySkills())
{
getOwner().addNewSkill(skill, false);
}
}
}
}
/**
* Удаляет у клана-владельца скилы резиденции
*/
protected void removeSkills()
{
if(getOwner() != null)
{
for(L2Skill skill : _skills)
{
getOwner().removeSkill(skill);
}
if(getType() == ResidenceType.Castle)
{
for(L2Skill skill : ((Castle) this).getTerritorySkills())
{
getOwner().removeSkill(skill);
}
}
getOwner().boarcastSkillListToOnlineMembers();
}
}
public ResidenceType getType()
{
return _type;
}
private int getNodeValue(Node node, String key)
{
if(node.getAttributes() == null)
{
return 0;
}
Node keyNode = node.getAttributes().getNamedItem(key);
return keyNode == null ? 0 : Integer.parseInt(keyNode.getNodeValue());
}
private ResidenceFunction checkAndGetFunction(int type)
{
ResidenceFunction function = getFunction(type);
if(function == null)
{
function = new ResidenceFunction(getId(), type);
_functions.add(function);
}
return function;
}
private void preLoadFunctions()
{
try
{
File file = new File(Config.DATAPACK_ROOT + "/data/residence.xml");
DocumentBuilderFactory factory1 = DocumentBuilderFactory.newInstance();
factory1.setValidating(false);
factory1.setIgnoringComments(true);
Document doc1 = factory1.newDocumentBuilder().parse(file);
for(Node n1 = doc1.getFirstChild(); n1 != null; n1 = n1.getNextSibling())
{
if("list".equalsIgnoreCase(n1.getNodeName()))
{
for(Node d1 = n1.getFirstChild(); d1 != null; d1 = d1.getNextSibling())
{
if("residence".equalsIgnoreCase(d1.getNodeName()) && Integer.parseInt(d1.getAttributes().getNamedItem("id").getNodeValue()) == getId())
{
for(Node s1 = d1.getFirstChild(); s1 != null; s1 = s1.getNextSibling())
{
int level = getNodeValue(s1, "level");
int lease = (int) (getNodeValue(s1, "lease") * Config.RESIDENCE_LEASE_FUNC_MULTIPLIER);
int npcId = getNodeValue(s1, "npcId");
int listId = getNodeValue(s1, "listId");
ResidenceFunction function = null;
if("teleport".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.TELEPORT);
function.addTeleports(level, parseTeleport(s1));
}
else if("support".equalsIgnoreCase(s1.getNodeName()))
{
if(level > 9 && !Config.ALT_CH_ALLOW_1H_BUFFS)
{
continue;
}
function = checkAndGetFunction(ResidenceFunction.SUPPORT);
function.addBuffs(level);
}
else if("item_create".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.ITEM_CREATE);
function.addBuylist(level, new int[]{npcId, listId});
}
else if("curtain".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.CURTAIN);
}
else if("platform".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.PLATFORM);
}
else if("restore_exp".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.RESTORE_EXP);
}
else if("restore_hp".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.RESTORE_HP);
}
else if("restore_mp".equalsIgnoreCase(s1.getNodeName()))
{
function = checkAndGetFunction(ResidenceFunction.RESTORE_MP);
}
if(function != null)
{
function.addLease(level, lease);
}
}
}
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
private TeleportLocation[] parseTeleport(Node s1)
{
GArray<TeleportLocation> targets = new GArray<TeleportLocation>();
for(Node t1 = s1.getFirstChild(); t1 != null; t1 = t1.getNextSibling())
{
if("target".equalsIgnoreCase(t1.getNodeName()))
{
String target = t1.getAttributes().getNamedItem("loc").getNodeValue();
String name = t1.getAttributes().getNamedItem("name").getNodeValue();
long price = Long.parseLong(t1.getAttributes().getNamedItem("price").getNodeValue());
int item = t1.getAttributes().getNamedItem("item") == null ? 57 : Integer.parseInt(t1.getAttributes().getNamedItem("item").getNodeValue());
TeleportLocation t = new TeleportLocation(target, item, price, name);
targets.add(t);
}
}
return targets.toArray(new TeleportLocation[targets.size()]);
}
private void loadFunctions()
{
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
ResultSet rs = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("SELECT * FROM residence_functions WHERE id = ?");
statement.setInt(1, getId());
rs = statement.executeQuery();
while(rs.next())
{
ResidenceFunction function = getFunction(rs.getInt("type"));
function.setLvl(rs.getInt("lvl"));
function.setEndTimeInMillis(rs.getInt("endTime") * 1000L);
function.setInDebt(rs.getBoolean("inDebt"));
function.setActive(true);
StartAutoTaskForFunction(function);
}
}
catch(Exception e)
{
_log.warning("Exception: SiegeUnit.loadFunctions(): " + e);
}
finally
{
DatabaseUtils.closeDatabaseCSR(con, statement, rs);
}
}
public boolean isFunctionActive(int type)
{
ResidenceFunction function = getFunction(type);
if(function != null && function.isActive() && function.getLevel() > 0)
{
return true;
}
return false;
}
public ResidenceFunction getFunction(int type)
{
for(int i = 0; i < _functions.size(); i++)
{
if(_functions.get(i).getType() == type)
{
return _functions.get(i);
}
}
return null;
}
public boolean updateFunctions(int type, int level)
{
L2Clan clan = getOwner();
if(clan == null)
{
return false;
}
long count = clan.getAdenaCount();
ResidenceFunction function = getFunction(type);
if(function == null)
{
return false;
}
if(function.isActive() && function.getLevel() == level)
{
return true;
}
int lease = level == 0 ? 0 : getFunction(type).getLease(level);
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
if(!function.isActive())
{
if(count >= lease)
{
clan.getWarehouse().destroyItem(57, lease);
}
else
{
return false;
}
long time = Calendar.getInstance().getTimeInMillis() + 86400000;
statement = con.prepareStatement("REPLACE residence_functions SET id=?, type=?, lvl=?, endTime=?");
statement.setInt(1, getId());
statement.setInt(2, type);
statement.setInt(3, level);
statement.setInt(4, (int) (time / 1000));
statement.execute();
function.setLvl(level);
function.setEndTimeInMillis(time);
function.setActive(true);
StartAutoTaskForFunction(function);
}
else
{
if(count >= lease - getFunction(type).getLease())
{
if(lease > getFunction(type).getLease())
{
clan.getWarehouse().destroyItem(57, lease - getFunction(type).getLease());
}
}
else
{
return false;
}
statement = con.prepareStatement("REPLACE residence_functions SET id=?, type=?, lvl=?");
statement.setInt(1, getId());
statement.setInt(2, type);
statement.setInt(3, level);
statement.execute();
function.setLvl(level);
}
}
catch(Exception e)
{
_log.warning("Exception: SiegeUnit.updateFunctions(int type, int lvl, int lease, long rate, long time, boolean addNew): " + e);
}
finally
{
DatabaseUtils.closeDatabaseCS(con, statement);
}
return true;
}
public void removeFunction(int type)
{
ThreadConnection con = null;
FiltredPreparedStatement statement = null;
try
{
con = L2DatabaseFactory.getInstance().getConnection();
statement = con.prepareStatement("DELETE FROM residence_functions WHERE id=? AND type=?");
statement.setInt(1, getId());
statement.setInt(2, type);
statement.execute();
}
catch(Exception e)
{
_log.warning("Exception: removeFunctions(int type): " + e);
}
finally
{
DatabaseUtils.closeDatabaseCS(con, statement);
}
}
private void StartAutoTaskForFunction(ResidenceFunction function)
{
if(getOwnerId() != 0)
{
try
{
L2Clan clan = getOwner();
if(clan == null)
{
_log.warning("SiegeUnit[485]: clan == null for residence " + getId());
return;
}
if(function.getEndTimeInMillis() > System.currentTimeMillis())
{
ThreadPoolManager.getInstance().scheduleGeneral(new AutoTaskForFunctions(function), function.getEndTimeInMillis() - System.currentTimeMillis());
}
else if(function.isInDebt() && clan.getAdenaCount() >= function.getLease()) // if player didn't pay before add extra fee
{
clan.getWarehouse().destroyItem(57, function.getLease());
function.updateRentTime(false);
ThreadPoolManager.getInstance().scheduleGeneral(new AutoTaskForFunctions(function), function.getEndTimeInMillis() - System.currentTimeMillis());
Log.add("deducted " + function.getLease() + " adena from " + getName() + " owner's residence for function type " + function.getType(), "residence");
}
else if(!function.isInDebt())
{
function.setInDebt(true);
function.updateRentTime(true);
ThreadPoolManager.getInstance().scheduleGeneral(new AutoTaskForFunctions(function), function.getEndTimeInMillis() - System.currentTimeMillis());
}
else
{
function.setLvl(0);
function.setActive(false);
removeFunction(function.getType());
Log.add("deactivate function type " + function.getType() + ", for " + getName() + " residence, because clan don't have enough money", "residence");
}
}
catch(Exception e)
{
System.out.println("StartAutoTaskForFunction: id: " + getId() + ", type: " + function.getType());
e.printStackTrace();
}
}
}
private class AutoTaskForFunctions implements Runnable
{
ResidenceFunction _function;
public AutoTaskForFunctions(ResidenceFunction function)
{
_function = function;
}
public void run()
{
StartAutoTaskForFunction(_function);
}
}
}