Fafurion branch.

This commit is contained in:
MobiusDev
2019-02-28 18:21:21 +00:00
parent 5224d41be9
commit e2a58ca111
19588 changed files with 3907251 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.model.L2Clan;
import com.l2jmobius.gameserver.model.entity.Fort;
import com.l2jmobius.gameserver.model.itemcontainer.Inventory;
/**
* Class managing periodical events with castle
* @author Vice - 2008
*/
public class FortUpdater implements Runnable
{
private static Logger LOGGER = Logger.getLogger(FortUpdater.class.getName());
private final L2Clan _clan;
private final Fort _fort;
private int _runCount;
private final UpdaterType _updaterType;
public enum UpdaterType
{
MAX_OWN_TIME, // gives fort back to NPC clan
PERIODIC_UPDATE // raise blood oath/supply level
}
public FortUpdater(Fort fort, L2Clan clan, int runCount, UpdaterType ut)
{
_fort = fort;
_clan = clan;
_runCount = runCount;
_updaterType = ut;
}
@Override
public void run()
{
try
{
switch (_updaterType)
{
case PERIODIC_UPDATE:
{
_runCount++;
if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan))
{
return;
}
_fort.getOwnerClan().increaseBloodOathCount();
if (_fort.getFortState() == 2)
{
if (_clan.getWarehouse().getAdena() >= Config.FS_FEE_FOR_CASTLE)
{
_clan.getWarehouse().destroyItemByItemId("FS_fee_for_Castle", Inventory.ADENA_ID, Config.FS_FEE_FOR_CASTLE, null, null);
_fort.getContractedCastle().addToTreasuryNoTax(Config.FS_FEE_FOR_CASTLE);
_fort.raiseSupplyLvL();
}
else
{
_fort.setFortState(1, 0);
}
}
_fort.saveFortVariables();
break;
}
case MAX_OWN_TIME:
{
if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan))
{
return;
}
if (_fort.getOwnedTime() > (Config.FS_MAX_OWN_TIME * 3600))
{
_fort.removeOwner(true);
_fort.setFortState(0, 0);
}
break;
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
}
}
public int getRunCount()
{
return _runCount;
}
}

View File

@@ -0,0 +1,509 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.time.Duration;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.Server;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.util.DeadLockDetector;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.data.sql.impl.AnnouncementsTable;
import com.l2jmobius.gameserver.data.sql.impl.CharNameTable;
import com.l2jmobius.gameserver.data.sql.impl.CharSummonTable;
import com.l2jmobius.gameserver.data.sql.impl.ClanTable;
import com.l2jmobius.gameserver.data.sql.impl.CrestTable;
import com.l2jmobius.gameserver.data.sql.impl.OfflineTradersTable;
import com.l2jmobius.gameserver.data.xml.impl.ActionData;
import com.l2jmobius.gameserver.data.xml.impl.AdminData;
import com.l2jmobius.gameserver.data.xml.impl.AlchemyData;
import com.l2jmobius.gameserver.data.xml.impl.AppearanceItemData;
import com.l2jmobius.gameserver.data.xml.impl.ArmorSetsData;
import com.l2jmobius.gameserver.data.xml.impl.AttendanceRewardData;
import com.l2jmobius.gameserver.data.xml.impl.BeautyShopData;
import com.l2jmobius.gameserver.data.xml.impl.BuyListData;
import com.l2jmobius.gameserver.data.xml.impl.CategoryData;
import com.l2jmobius.gameserver.data.xml.impl.ClanHallData;
import com.l2jmobius.gameserver.data.xml.impl.ClanMasteryData;
import com.l2jmobius.gameserver.data.xml.impl.ClanRewardData;
import com.l2jmobius.gameserver.data.xml.impl.ClanShopData;
import com.l2jmobius.gameserver.data.xml.impl.ClassListData;
import com.l2jmobius.gameserver.data.xml.impl.CombinationItemsData;
import com.l2jmobius.gameserver.data.xml.impl.CubicData;
import com.l2jmobius.gameserver.data.xml.impl.DailyMissionData;
import com.l2jmobius.gameserver.data.xml.impl.DoorData;
import com.l2jmobius.gameserver.data.xml.impl.EnchantItemData;
import com.l2jmobius.gameserver.data.xml.impl.EnchantItemGroupsData;
import com.l2jmobius.gameserver.data.xml.impl.EnchantItemHPBonusData;
import com.l2jmobius.gameserver.data.xml.impl.EnchantItemOptionsData;
import com.l2jmobius.gameserver.data.xml.impl.EnchantSkillGroupsData;
import com.l2jmobius.gameserver.data.xml.impl.EnsoulData;
import com.l2jmobius.gameserver.data.xml.impl.EventEngineData;
import com.l2jmobius.gameserver.data.xml.impl.ExperienceData;
import com.l2jmobius.gameserver.data.xml.impl.ExtendDropData;
import com.l2jmobius.gameserver.data.xml.impl.FakePlayerData;
import com.l2jmobius.gameserver.data.xml.impl.FenceData;
import com.l2jmobius.gameserver.data.xml.impl.FishingData;
import com.l2jmobius.gameserver.data.xml.impl.HennaData;
import com.l2jmobius.gameserver.data.xml.impl.HitConditionBonusData;
import com.l2jmobius.gameserver.data.xml.impl.InitialEquipmentData;
import com.l2jmobius.gameserver.data.xml.impl.InitialShortcutData;
import com.l2jmobius.gameserver.data.xml.impl.ItemCrystallizationData;
import com.l2jmobius.gameserver.data.xml.impl.KarmaData;
import com.l2jmobius.gameserver.data.xml.impl.LuckyGameData;
import com.l2jmobius.gameserver.data.xml.impl.MonsterBookData;
import com.l2jmobius.gameserver.data.xml.impl.MultisellData;
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
import com.l2jmobius.gameserver.data.xml.impl.OptionData;
import com.l2jmobius.gameserver.data.xml.impl.PetDataTable;
import com.l2jmobius.gameserver.data.xml.impl.PetSkillData;
import com.l2jmobius.gameserver.data.xml.impl.PlayerTemplateData;
import com.l2jmobius.gameserver.data.xml.impl.PlayerXpPercentLostData;
import com.l2jmobius.gameserver.data.xml.impl.PrimeShopData;
import com.l2jmobius.gameserver.data.xml.impl.RecipeData;
import com.l2jmobius.gameserver.data.xml.impl.ResidenceFunctionsData;
import com.l2jmobius.gameserver.data.xml.impl.SayuneData;
import com.l2jmobius.gameserver.data.xml.impl.SecondaryAuthData;
import com.l2jmobius.gameserver.data.xml.impl.ShuttleData;
import com.l2jmobius.gameserver.data.xml.impl.SiegeScheduleData;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.data.xml.impl.SkillTreesData;
import com.l2jmobius.gameserver.data.xml.impl.SpawnsData;
import com.l2jmobius.gameserver.data.xml.impl.StaticObjectData;
import com.l2jmobius.gameserver.data.xml.impl.TeleportersData;
import com.l2jmobius.gameserver.data.xml.impl.TransformData;
import com.l2jmobius.gameserver.data.xml.impl.VariationData;
import com.l2jmobius.gameserver.datatables.BotReportTable;
import com.l2jmobius.gameserver.datatables.EventDroplist;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.handler.ConditionHandler;
import com.l2jmobius.gameserver.handler.DailyMissionHandler;
import com.l2jmobius.gameserver.handler.EffectHandler;
import com.l2jmobius.gameserver.handler.SkillConditionHandler;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.instancemanager.AirShipManager;
import com.l2jmobius.gameserver.instancemanager.AntiFeedManager;
import com.l2jmobius.gameserver.instancemanager.BoatManager;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.CastleManorManager;
import com.l2jmobius.gameserver.instancemanager.ClanEntryManager;
import com.l2jmobius.gameserver.instancemanager.ClanHallAuctionManager;
import com.l2jmobius.gameserver.instancemanager.CommissionManager;
import com.l2jmobius.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jmobius.gameserver.instancemanager.DBSpawnManager;
import com.l2jmobius.gameserver.instancemanager.FactionManager;
import com.l2jmobius.gameserver.instancemanager.FakePlayerChatManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
import com.l2jmobius.gameserver.instancemanager.FortSiegeManager;
import com.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
import com.l2jmobius.gameserver.instancemanager.GraciaSeedsManager;
import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
import com.l2jmobius.gameserver.instancemanager.InstanceManager;
import com.l2jmobius.gameserver.instancemanager.ItemAuctionManager;
import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jmobius.gameserver.instancemanager.MailManager;
import com.l2jmobius.gameserver.instancemanager.MapRegionManager;
import com.l2jmobius.gameserver.instancemanager.MatchingRoomManager;
import com.l2jmobius.gameserver.instancemanager.MentorManager;
import com.l2jmobius.gameserver.instancemanager.PcCafePointsManager;
import com.l2jmobius.gameserver.instancemanager.PetitionManager;
import com.l2jmobius.gameserver.instancemanager.PremiumManager;
import com.l2jmobius.gameserver.instancemanager.PunishmentManager;
import com.l2jmobius.gameserver.instancemanager.QuestManager;
import com.l2jmobius.gameserver.instancemanager.SellBuffsManager;
import com.l2jmobius.gameserver.instancemanager.ServerRestartManager;
import com.l2jmobius.gameserver.instancemanager.SiegeGuardManager;
import com.l2jmobius.gameserver.instancemanager.SiegeManager;
import com.l2jmobius.gameserver.instancemanager.WalkingManager;
import com.l2jmobius.gameserver.instancemanager.ZoneManager;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.entity.Hero;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.olympiad.Olympiad;
import com.l2jmobius.gameserver.model.votereward.VoteSystem;
import com.l2jmobius.gameserver.network.ClientNetworkManager;
import com.l2jmobius.gameserver.network.loginserver.LoginServerNetworkManager;
import com.l2jmobius.gameserver.network.telnet.TelnetServer;
import com.l2jmobius.gameserver.scripting.ScriptEngineManager;
import com.l2jmobius.gameserver.taskmanager.TaskManager;
import com.l2jmobius.gameserver.ui.Gui;
import com.l2jmobius.gameserver.util.Broadcast;
public class GameServer
{
private static final Logger LOGGER = Logger.getLogger(GameServer.class.getName());
private final DeadLockDetector _deadDetectThread;
private static GameServer INSTANCE;
public static final Calendar dateTimeServerStarted = Calendar.getInstance();
public long getUsedMemoryMB()
{
return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1048576;
}
public DeadLockDetector getDeadLockDetectorThread()
{
return _deadDetectThread;
}
public GameServer() throws Exception
{
final long serverLoadStart = System.currentTimeMillis();
Server.serverMode = Server.MODE_GAMESERVER;
// GUI
if (!GraphicsEnvironment.isHeadless())
{
System.out.println("GameServer: Running in GUI mode.");
new Gui();
}
// Create log folder
final File logFolder = new File(Config.DATAPACK_ROOT, "log");
logFolder.mkdir();
// Create input stream for log file -- or store file data into memory
try (InputStream is = new FileInputStream(new File("./log.cfg")))
{
LogManager.getLogManager().readConfiguration(is);
}
// Initialize config
Config.load();
printSection("Database");
DatabaseFactory.init();
printSection("ThreadPool");
ThreadPool.init();
printSection("IdFactory");
if (!IdFactory.getInstance().isInitialized())
{
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
throw new Exception("Could not initialize the ID factory!");
}
// load script engines
printSection("Scripting Engine");
EventDispatcher.getInstance();
ScriptEngineManager.getInstance();
printSection("Telnet");
TelnetServer.getInstance();
printSection("World");
// start game time control early
GameTimeController.init();
L2World.getInstance();
MapRegionManager.getInstance();
ZoneManager.getInstance();
DoorData.getInstance();
FenceData.getInstance();
AnnouncementsTable.getInstance();
GlobalVariablesManager.getInstance();
printSection("Data");
ActionData.getInstance();
CategoryData.getInstance();
SecondaryAuthData.getInstance();
CombinationItemsData.getInstance();
SayuneData.getInstance();
ClanRewardData.getInstance();
DailyMissionHandler.getInstance().executeScript();
DailyMissionData.getInstance();
printSection("Skills");
SkillConditionHandler.getInstance().executeScript();
EffectHandler.getInstance().executeScript();
EnchantSkillGroupsData.getInstance();
SkillTreesData.getInstance();
SkillData.getInstance();
PetSkillData.getInstance();
printSection("Items");
ConditionHandler.getInstance().executeScript();
ItemTable.getInstance();
EnchantItemGroupsData.getInstance();
EnchantItemData.getInstance();
EnchantItemOptionsData.getInstance();
ItemCrystallizationData.getInstance();
OptionData.getInstance();
VariationData.getInstance();
EnsoulData.getInstance();
EnchantItemHPBonusData.getInstance();
BuyListData.getInstance();
MultisellData.getInstance();
RecipeData.getInstance();
ArmorSetsData.getInstance();
FishingData.getInstance();
HennaData.getInstance();
PrimeShopData.getInstance();
PcCafePointsManager.getInstance();
AppearanceItemData.getInstance();
AlchemyData.getInstance();
CommissionManager.getInstance();
LuckyGameData.getInstance();
AttendanceRewardData.getInstance();
printSection("Characters");
ClassListData.getInstance();
InitialEquipmentData.getInstance();
InitialShortcutData.getInstance();
ExperienceData.getInstance();
PlayerXpPercentLostData.getInstance();
KarmaData.getInstance();
HitConditionBonusData.getInstance();
PlayerTemplateData.getInstance();
CharNameTable.getInstance();
AdminData.getInstance();
PetDataTable.getInstance();
CubicData.getInstance();
CharSummonTable.getInstance().init();
BeautyShopData.getInstance();
MentorManager.getInstance();
if (Config.FACTION_SYSTEM_ENABLED)
{
FactionManager.getInstance();
}
if (Config.PREMIUM_SYSTEM_ENABLED)
{
LOGGER.info("PremiumManager: Premium system is enabled.");
PremiumManager.getInstance();
}
printSection("Clans");
ClanTable.getInstance();
ResidenceFunctionsData.getInstance();
ClanHallData.getInstance();
ClanHallAuctionManager.getInstance();
ClanEntryManager.getInstance();
ClanMasteryData.getInstance();
ClanShopData.getInstance();
printSection("Geodata");
long geodataMemory = getUsedMemoryMB();
GeoEngine.getInstance();
geodataMemory = getUsedMemoryMB() - geodataMemory;
if (geodataMemory < 0)
{
geodataMemory = 0;
}
printSection("NPCs");
NpcData.getInstance();
FakePlayerData.getInstance();
FakePlayerChatManager.getInstance();
ExtendDropData.getInstance();
SpawnsData.getInstance();
MonsterBookData.getInstance();
WalkingManager.getInstance();
StaticObjectData.getInstance();
ItemAuctionManager.getInstance();
CastleManager.getInstance().loadInstances();
GrandBossManager.getInstance();
EventDroplist.getInstance();
printSection("Instance");
InstanceManager.getInstance();
printSection("Olympiad");
Olympiad.getInstance();
Hero.getInstance();
// Call to load caches
printSection("Cache");
HtmCache.getInstance();
CrestTable.getInstance();
TeleportersData.getInstance();
MatchingRoomManager.getInstance();
PetitionManager.getInstance();
CursedWeaponsManager.getInstance();
TransformData.getInstance();
BotReportTable.getInstance();
if (Config.SELLBUFF_ENABLED)
{
SellBuffsManager.getInstance();
}
printSection("Scripts");
QuestManager.getInstance();
BoatManager.getInstance();
AirShipManager.getInstance();
ShuttleData.getInstance();
GraciaSeedsManager.getInstance();
try
{
LOGGER.info(getClass().getSimpleName() + ": Loading server scripts:");
ScriptEngineManager.getInstance().executeMasterHandler();
ScriptEngineManager.getInstance().executeScriptList();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Failed to execute script list!", e);
}
SpawnsData.getInstance().init();
DBSpawnManager.getInstance();
printSection("Event Engine");
EventEngineData.getInstance();
VoteSystem.initialize();
printSection("Siege");
SiegeManager.getInstance().getSieges();
CastleManager.getInstance().activateInstances();
FortManager.getInstance().loadInstances();
FortManager.getInstance().activateInstances();
FortSiegeManager.getInstance();
SiegeScheduleData.getInstance();
CastleManorManager.getInstance();
SiegeGuardManager.getInstance();
QuestManager.getInstance().report();
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance();
}
if ((Config.AUTODESTROY_ITEM_AFTER > 0) || (Config.HERB_AUTO_DESTROY_TIME > 0))
{
ItemsAutoDestroy.getInstance();
}
if (Config.ALLOW_RACE)
{
MonsterRace.getInstance();
}
TaskManager.getInstance();
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);
if (Config.ALLOW_MAIL)
{
MailManager.getInstance();
}
PunishmentManager.getInstance();
Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());
LOGGER.info("IdFactory: Free ObjectID's remaining: " + IdFactory.getInstance().size());
if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
{
OfflineTradersTable.getInstance().restoreOfflineTraders();
}
if (Config.SERVER_RESTART_SCHEDULE_ENABLED)
{
ServerRestartManager.getInstance();
}
if (Config.DEADLOCK_DETECTOR)
{
_deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () ->
{
if (Config.RESTART_ON_DEADLOCK)
{
Broadcast.toAllOnlinePlayers("Server has stability issues - restarting now.");
Shutdown.getInstance().startShutdown(null, 60, true);
}
});
_deadDetectThread.setDaemon(true);
_deadDetectThread.start();
}
else
{
_deadDetectThread = null;
}
System.gc();
final long totalMem = Runtime.getRuntime().maxMemory() / 1048576;
LOGGER.info(getClass().getSimpleName() + ": Started, using " + getUsedMemoryMB() + " of " + totalMem + " MB total memory.");
LOGGER.info(getClass().getSimpleName() + ": Geodata use " + geodataMemory + " MB of memory.");
LOGGER.info(getClass().getSimpleName() + ": Maximum number of connected players is " + Config.MAXIMUM_ONLINE_USERS + ".");
LOGGER.info(getClass().getSimpleName() + ": Server loaded in " + ((System.currentTimeMillis() - serverLoadStart) / 1000) + " seconds.");
ClientNetworkManager.getInstance().start();
if (Boolean.getBoolean("newLoginServer"))
{
LoginServerNetworkManager.getInstance().connect();
}
else
{
LoginServerThread.getInstance().start();
}
Toolkit.getDefaultToolkit().beep();
}
public long getStartedTime()
{
return ManagementFactory.getRuntimeMXBean().getStartTime();
}
public String getUptime()
{
final long uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000;
final long hours = uptime / 3600;
final long mins = (uptime - (hours * 3600)) / 60;
final long secs = ((uptime - (hours * 3600)) - (mins * 60));
if (hours > 0)
{
return hours + "hrs " + mins + "mins " + secs + "secs";
}
return mins + "mins " + secs + "secs";
}
public static void main(String[] args) throws Exception
{
INSTANCE = new GameServer();
}
private static void printSection(String s)
{
s = "=[ " + s + " ]";
while (s.length() < 61)
{
s = "-" + s;
}
LOGGER.info(s);
}
public static GameServer getInstance()
{
return INSTANCE;
}
}

View File

@@ -0,0 +1,221 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.util.Calendar;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.OnDayNightChange;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* Game Time controller class.
* @author Forsaiken
*/
public final class GameTimeController extends Thread
{
private static final Logger LOGGER = Logger.getLogger(GameTimeController.class.getName());
public static final int TICKS_PER_SECOND = 10; // not able to change this without checking through code
public static final int MILLIS_IN_TICK = 1000 / TICKS_PER_SECOND;
public static final int IG_DAYS_PER_DAY = 6;
public static final int MILLIS_PER_IG_DAY = (3600000 * 24) / IG_DAYS_PER_DAY;
public static final int SECONDS_PER_IG_DAY = MILLIS_PER_IG_DAY / 1000;
public static final int TICKS_PER_IG_DAY = SECONDS_PER_IG_DAY * TICKS_PER_SECOND;
private final static int SHADOW_SENSE_ID = 294;
private static GameTimeController _instance;
private final Set<L2Character> _movingObjects = ConcurrentHashMap.newKeySet();
private final Set<L2Character> _shadowSenseCharacters = ConcurrentHashMap.newKeySet();
private final long _referenceTime;
private GameTimeController()
{
super("GameTimeController");
super.setDaemon(true);
super.setPriority(MAX_PRIORITY);
final Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
_referenceTime = c.getTimeInMillis();
super.start();
}
public static void init()
{
_instance = new GameTimeController();
}
public final int getGameTime()
{
return (getGameTicks() % TICKS_PER_IG_DAY) / MILLIS_IN_TICK;
}
public final int getGameHour()
{
return getGameTime() / 60;
}
public final int getGameMinute()
{
return getGameTime() % 60;
}
public final boolean isNight()
{
return getGameHour() < 6;
}
/**
* The true GameTime tick. Directly taken from current time. This represents the tick of the time.
* @return
*/
public final int getGameTicks()
{
return (int) ((System.currentTimeMillis() - _referenceTime) / MILLIS_IN_TICK);
}
/**
* Add a L2Character to movingObjects of GameTimeController.
* @param cha The L2Character to add to movingObjects of GameTimeController
*/
public final void registerMovingObject(L2Character cha)
{
if (cha == null)
{
return;
}
if (!_movingObjects.contains(cha))
{
_movingObjects.add(cha);
}
}
/**
* Move all L2Characters contained in movingObjects of GameTimeController.<BR>
* <B><U> Concept</U> :</B><BR>
* All L2Character in movement are identified in <B>movingObjects</B> of GameTimeController.<BR>
* <B><U> Actions</U> :</B><BR>
* <ul>
* <li>Update the position of each L2Character</li>
* <li>If movement is finished, the L2Character is removed from movingObjects</li>
* <li>Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with EVT_ARRIVED</li>
* </ul>
*/
private void moveObjects()
{
_movingObjects.removeIf(L2Character::updatePosition);
}
public final void stopTimer()
{
super.interrupt();
LOGGER.info(getClass().getSimpleName() + ": Stopped.");
}
@Override
public final void run()
{
LOGGER.info(getClass().getSimpleName() + ": Started.");
long nextTickTime;
long sleepTime;
boolean isNight = isNight();
EventDispatcher.getInstance().notifyEventAsync(new OnDayNightChange(isNight));
while (true)
{
nextTickTime = ((System.currentTimeMillis() / MILLIS_IN_TICK) * MILLIS_IN_TICK) + 100;
try
{
moveObjects();
}
catch (Throwable e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName(), e);
}
sleepTime = nextTickTime - System.currentTimeMillis();
if (sleepTime > 0)
{
try
{
Thread.sleep(sleepTime);
}
catch (InterruptedException e)
{
}
}
if (isNight() != isNight)
{
isNight = !isNight;
EventDispatcher.getInstance().notifyEventAsync(new OnDayNightChange(isNight));
notifyShadowSense();
}
}
}
public synchronized void addShadowSenseCharacter(L2Character character)
{
if (!_shadowSenseCharacters.contains(character))
{
_shadowSenseCharacters.add(character);
if (isNight())
{
final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.IT_IS_NOW_MIDNIGHT_AND_THE_EFFECT_OF_S1_CAN_BE_FELT);
msg.addSkillName(SHADOW_SENSE_ID);
character.sendPacket(msg);
}
}
}
public void removeShadowSenseCharacter(L2Character character)
{
_shadowSenseCharacters.remove(character);
}
private void notifyShadowSense()
{
final SystemMessage msg = SystemMessage.getSystemMessage(isNight() ? SystemMessageId.IT_IS_NOW_MIDNIGHT_AND_THE_EFFECT_OF_S1_CAN_BE_FELT : SystemMessageId.IT_IS_DAWN_AND_THE_EFFECT_OF_S1_WILL_NOW_DISAPPEAR);
msg.addSkillName(SHADOW_SENSE_ID);
for (L2Character character : _shadowSenseCharacters)
{
character.getStat().recalculateStats(true);
character.sendPacket(msg);
}
}
public static GameTimeController getInstance()
{
return _instance;
}
}

View File

@@ -0,0 +1,40 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
/**
* Interface for managers of list of instances.
* @author fordfrog
*/
public interface InstanceListManager
{
/**
* Loads instances with their data from persistent format.<br>
* This method has no side effect as calling methods of another instance manager.
*/
void loadInstances();
/**
* For each loaded instance, updates references to related instances.
*/
void updateReferences();
/**
* Activates instances so their setup is performed.
*/
void activateInstances();
}

View File

@@ -0,0 +1,98 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.gameserver.enums.ItemLocation;
import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
public final class ItemsAutoDestroy
{
private final List<L2ItemInstance> _items = new LinkedList<>();
protected ItemsAutoDestroy()
{
ThreadPool.scheduleAtFixedRate(this::removeItems, 5000, 5000);
}
public static ItemsAutoDestroy getInstance()
{
return SingletonHolder._instance;
}
public synchronized void addItem(L2ItemInstance item)
{
item.setDropTime(System.currentTimeMillis());
_items.add(item);
}
private synchronized void removeItems()
{
if (_items.isEmpty())
{
return;
}
final long curtime = System.currentTimeMillis();
final Iterator<L2ItemInstance> itemIterator = _items.iterator();
while (itemIterator.hasNext())
{
final L2ItemInstance item = itemIterator.next();
if ((item.getDropTime() == 0) || (item.getItemLocation() != ItemLocation.VOID))
{
itemIterator.remove();
}
else
{
final long autoDestroyTime;
if (item.getItem().getAutoDestroyTime() > 0)
{
autoDestroyTime = item.getItem().getAutoDestroyTime();
}
else if (item.getItem().hasExImmediateEffect())
{
autoDestroyTime = Config.HERB_AUTO_DESTROY_TIME;
}
else
{
autoDestroyTime = ((Config.AUTODESTROY_ITEM_AFTER == 0) ? 3600000 : Config.AUTODESTROY_ITEM_AFTER * 1000);
}
if ((curtime - item.getDropTime()) > autoDestroyTime)
{
item.decayMe();
itemIterator.remove();
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().removeObject(item);
}
}
}
}
}
private static class SingletonHolder
{
protected static final ItemsAutoDestroy _instance = new ItemsAutoDestroy();
}
}

View File

@@ -0,0 +1,842 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPublicKeySpec;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.network.BaseSendablePacket;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.commons.util.crypt.NewCrypt;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.ConnectionState;
import com.l2jmobius.gameserver.network.L2GameClient;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.loginserverpackets.game.AuthRequest;
import com.l2jmobius.gameserver.network.loginserverpackets.game.BlowFishKey;
import com.l2jmobius.gameserver.network.loginserverpackets.game.ChangeAccessLevel;
import com.l2jmobius.gameserver.network.loginserverpackets.game.ChangePassword;
import com.l2jmobius.gameserver.network.loginserverpackets.game.PlayerAuthRequest;
import com.l2jmobius.gameserver.network.loginserverpackets.game.PlayerInGame;
import com.l2jmobius.gameserver.network.loginserverpackets.game.PlayerLogout;
import com.l2jmobius.gameserver.network.loginserverpackets.game.PlayerTracert;
import com.l2jmobius.gameserver.network.loginserverpackets.game.ReplyCharacters;
import com.l2jmobius.gameserver.network.loginserverpackets.game.SendMail;
import com.l2jmobius.gameserver.network.loginserverpackets.game.ServerStatus;
import com.l2jmobius.gameserver.network.loginserverpackets.game.TempBan;
import com.l2jmobius.gameserver.network.loginserverpackets.login.AuthResponse;
import com.l2jmobius.gameserver.network.loginserverpackets.login.ChangePasswordResponse;
import com.l2jmobius.gameserver.network.loginserverpackets.login.InitLS;
import com.l2jmobius.gameserver.network.loginserverpackets.login.KickPlayer;
import com.l2jmobius.gameserver.network.loginserverpackets.login.LoginServerFail;
import com.l2jmobius.gameserver.network.loginserverpackets.login.PlayerAuthResponse;
import com.l2jmobius.gameserver.network.loginserverpackets.login.RequestCharacters;
import com.l2jmobius.gameserver.network.serverpackets.CharSelectionInfo;
import com.l2jmobius.gameserver.network.serverpackets.LoginFail;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
public class LoginServerThread extends Thread
{
protected static final Logger LOGGER = Logger.getLogger(LoginServerThread.class.getName());
protected static final Logger ACCOUNTING_LOGGER = Logger.getLogger("accounting");
/**
* @see com.l2jmobius.loginserver.LoginServer#PROTOCOL_REV
*/
private static final int REVISION = 0x0106;
private final String _hostname;
private final int _port;
private final int _gamePort;
private Socket _loginSocket;
private OutputStream _out;
/**
* The BlowFish engine used to encrypt packets<br>
* It is first initialized with a unified key:<br>
* "_;v.]05-31!|+-%xT!^[$\00"<br>
* <br>
* and then after handshake, with a new key sent by<br>
* login server during the handshake. This new key is stored<br>
* in blowfishKey
*/
private NewCrypt _blowfish;
private byte[] _hexID;
private final boolean _acceptAlternate;
private int _requestID;
private final boolean _reserveHost;
private int _maxPlayer;
private final Set<WaitingClient> _waitingClients = ConcurrentHashMap.newKeySet();
private final Map<String, L2GameClient> _accountsInGameServer = new ConcurrentHashMap<>();
private int _status;
private String _serverName;
private final List<String> _subnets;
private final List<String> _hosts;
/**
* Instantiates a new login server thread.
*/
protected LoginServerThread()
{
super("LoginServerThread");
_port = Config.GAME_SERVER_LOGIN_PORT;
_gamePort = Config.PORT_GAME;
_hostname = Config.GAME_SERVER_LOGIN_HOST;
_hexID = Config.HEX_ID;
if (_hexID == null)
{
_requestID = Config.REQUEST_ID;
_hexID = CommonUtil.generateHex(16);
}
else
{
_requestID = Config.SERVER_ID;
}
_acceptAlternate = Config.ACCEPT_ALTERNATE_ID;
_reserveHost = Config.RESERVE_HOST_ON_LOGIN;
_subnets = Config.GAME_SERVER_SUBNETS;
_hosts = Config.GAME_SERVER_HOSTS;
_maxPlayer = Config.MAXIMUM_ONLINE_USERS;
}
/**
* Gets the single instance of LoginServerThread.
* @return single instance of LoginServerThread
*/
public static LoginServerThread getInstance()
{
return SingletonHolder._instance;
}
@Override
public void run()
{
while (!isInterrupted())
{
int lengthHi = 0;
int lengthLo = 0;
int length = 0;
boolean checksumOk = false;
try
{
// Connection
LOGGER.info(getClass().getSimpleName() + ": Connecting to login on " + _hostname + ":" + _port);
_loginSocket = new Socket(_hostname, _port);
final InputStream in = _loginSocket.getInputStream();
_out = new BufferedOutputStream(_loginSocket.getOutputStream());
// init Blowfish
final byte[] blowfishKey = CommonUtil.generateHex(40);
_blowfish = new NewCrypt("_;v.]05-31!|+-%xT!^[$\00");
while (!isInterrupted())
{
lengthLo = in.read();
lengthHi = in.read();
length = (lengthHi * 256) + lengthLo;
if (lengthHi < 0)
{
LOGGER.finer(getClass().getSimpleName() + ": Login terminated the connection.");
break;
}
final byte[] incoming = new byte[length - 2];
int receivedBytes = 0;
int newBytes = 0;
int left = length - 2;
while ((newBytes != -1) && (receivedBytes < (length - 2)))
{
newBytes = in.read(incoming, receivedBytes, left);
receivedBytes += newBytes;
left -= newBytes;
}
if (receivedBytes != (length - 2))
{
LOGGER.warning(getClass().getSimpleName() + ": Incomplete Packet is sent to the server, closing connection.(LS)");
break;
}
// decrypt if we have a key
_blowfish.decrypt(incoming, 0, incoming.length);
checksumOk = NewCrypt.verifyChecksum(incoming);
if (!checksumOk)
{
LOGGER.warning(getClass().getSimpleName() + ": Incorrect packet checksum, ignoring packet (LS)");
break;
}
final int packetType = incoming[0] & 0xff;
switch (packetType)
{
case 0x00:
{
final InitLS init = new InitLS(incoming);
if (init.getRevision() != REVISION)
{
// TODO: revision mismatch
LOGGER.warning("/!\\ Revision mismatch between LS and GS /!\\");
break;
}
RSAPublicKey publicKey;
try
{
final KeyFactory kfac = KeyFactory.getInstance("RSA");
final BigInteger modulus = new BigInteger(init.getRSAKey());
final RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4);
publicKey = (RSAPublicKey) kfac.generatePublic(kspec1);
}
catch (GeneralSecurityException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Trouble while init the public key send by login");
break;
}
// send the blowfish key through the rsa encryption
sendPacket(new BlowFishKey(blowfishKey, publicKey));
// now, only accept packet with the new encryption
_blowfish = new NewCrypt(blowfishKey);
sendPacket(new AuthRequest(_requestID, _acceptAlternate, _hexID, _gamePort, _reserveHost, _maxPlayer, _subnets, _hosts));
break;
}
case 0x01:
{
final LoginServerFail lsf = new LoginServerFail(incoming);
LOGGER.info(getClass().getSimpleName() + ": Damn! Registeration Failed: " + lsf.getReasonString());
// login will close the connection here
break;
}
case 0x02:
{
final AuthResponse aresp = new AuthResponse(incoming);
final int serverID = aresp.getServerId();
_serverName = aresp.getServerName();
Config.saveHexid(serverID, hexToString(_hexID));
LOGGER.info(getClass().getSimpleName() + ": Registered on login as Server " + serverID + ": " + _serverName);
final ServerStatus st = new ServerStatus();
if (Config.SERVER_LIST_BRACKET)
{
st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.ON);
}
else
{
st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.OFF);
}
st.addAttribute(ServerStatus.SERVER_TYPE, Config.SERVER_LIST_TYPE);
if (Config.SERVER_GMONLY)
{
st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY);
}
else
{
st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO);
}
if (Config.SERVER_LIST_AGE == 15)
{
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_15);
}
else if (Config.SERVER_LIST_AGE == 18)
{
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_18);
}
else
{
st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_ALL);
}
sendPacket(st);
final List<String> playerList = L2World.getInstance().getPlayers().stream().filter(player -> !player.isInOfflineMode()).map(L2PcInstance::getAccountName).collect(Collectors.toList());
if (!playerList.isEmpty())
{
sendPacket(new PlayerInGame(playerList));
}
break;
}
case 0x03:
{
final PlayerAuthResponse par = new PlayerAuthResponse(incoming);
final String account = par.getAccount();
WaitingClient wcToRemove = null;
synchronized (_waitingClients)
{
for (WaitingClient wc : _waitingClients)
{
if (wc.account.equals(account))
{
wcToRemove = wc;
}
}
}
if (wcToRemove != null)
{
if (par.isAuthed())
{
final PlayerInGame pig = new PlayerInGame(par.getAccount());
sendPacket(pig);
wcToRemove.gameClient.setConnectionState(ConnectionState.AUTHENTICATED);
wcToRemove.gameClient.setSessionId(wcToRemove.session);
wcToRemove.gameClient.sendPacket(LoginFail.LOGIN_SUCCESS);
final CharSelectionInfo cl = new CharSelectionInfo(wcToRemove.account, wcToRemove.gameClient.getSessionId().playOkID1);
wcToRemove.gameClient.sendPacket(cl);
wcToRemove.gameClient.setCharSelection(cl.getCharInfo());
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Session key is not correct. Closing connection for account " + wcToRemove.account);
// wcToRemove.gameClient.getConnection().sendPacket(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
wcToRemove.gameClient.close(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER));
_accountsInGameServer.remove(wcToRemove.account);
}
_waitingClients.remove(wcToRemove);
}
break;
}
case 0x04:
{
final KickPlayer kp = new KickPlayer(incoming);
doKickPlayer(kp.getAccount());
break;
}
case 0x05:
{
final RequestCharacters rc = new RequestCharacters(incoming);
getCharsOnServer(rc.getAccount());
break;
}
case 0x06:
{
new ChangePasswordResponse(incoming);
break;
}
}
}
}
catch (UnknownHostException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": ", e);
}
catch (SocketException e)
{
LOGGER.warning(getClass().getSimpleName() + ": LoginServer not avaible, trying to reconnect...");
}
catch (IOException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Disconnected from Login, Trying to reconnect: ", e);
}
finally
{
try
{
_loginSocket.close();
if (isInterrupted())
{
return;
}
}
catch (Exception e)
{
}
}
try
{
Thread.sleep(5000); // 5 seconds tempo.
}
catch (InterruptedException e)
{
return; // never swallow an interrupt!
}
}
}
/**
* Adds the waiting client and send request.
* @param acc the account
* @param client the game client
* @param key the session key
*/
public void addWaitingClientAndSendRequest(String acc, L2GameClient client, SessionKey key)
{
final WaitingClient wc = new WaitingClient(acc, client, key);
synchronized (_waitingClients)
{
_waitingClients.add(wc);
}
final PlayerAuthRequest par = new PlayerAuthRequest(acc, key);
try
{
sendPacket(par);
}
catch (IOException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Error while sending player auth request");
}
}
/**
* Removes the waiting client.
* @param client the client
*/
public void removeWaitingClient(L2GameClient client)
{
WaitingClient toRemove = null;
synchronized (_waitingClients)
{
for (WaitingClient c : _waitingClients)
{
if (c.gameClient == client)
{
toRemove = c;
}
}
if (toRemove != null)
{
_waitingClients.remove(toRemove);
}
}
}
/**
* Send logout for the given account.
* @param account the account
*/
public void sendLogout(String account)
{
if (account == null)
{
return;
}
final PlayerLogout pl = new PlayerLogout(account);
try
{
sendPacket(pl);
}
catch (IOException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Error while sending logout packet to login");
}
finally
{
_accountsInGameServer.remove(account);
}
}
/**
* Adds the game server login.
* @param account the account
* @param client the client
* @return {@code true} if account was not already logged in, {@code false} otherwise
*/
public boolean addGameServerLogin(String account, L2GameClient client)
{
return _accountsInGameServer.putIfAbsent(account, client) == null;
}
/**
* Send access level.
* @param account the account
* @param level the access level
*/
public void sendAccessLevel(String account, int level)
{
final ChangeAccessLevel cal = new ChangeAccessLevel(account, level);
try
{
sendPacket(cal);
}
catch (IOException e)
{
}
}
/**
* Send client tracert.
* @param account the account
* @param address the address
*/
public void sendClientTracert(String account, String[] address)
{
final PlayerTracert ptc = new PlayerTracert(account, address[0], address[1], address[2], address[3], address[4]);
try
{
sendPacket(ptc);
}
catch (IOException e)
{
}
}
/**
* Send mail.
* @param account the account
* @param mailId the mail id
* @param args the args
*/
public void sendMail(String account, String mailId, String... args)
{
final SendMail sem = new SendMail(account, mailId, args);
try
{
sendPacket(sem);
}
catch (IOException e)
{
}
}
/**
* Send temp ban.
* @param account the account
* @param ip the ip
* @param time the time
*/
public void sendTempBan(String account, String ip, long time)
{
final TempBan tbn = new TempBan(account, ip, time);
try
{
sendPacket(tbn);
}
catch (IOException e)
{
}
}
/**
* Hex to string.
* @param hex the hex value
* @return the hex value as string
*/
private String hexToString(byte[] hex)
{
return new BigInteger(hex).toString(16);
}
/**
* Kick player for the given account.
* @param account the account
*/
private void doKickPlayer(String account)
{
final L2GameClient client = _accountsInGameServer.get(account);
if (client != null)
{
if (client.isDetached())
{
if (client.getActiveChar() != null)
{
client.getActiveChar().deleteMe();
}
}
else
{
ACCOUNTING_LOGGER.info("Kicked by login, " + client);
}
client.close(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_LOGGED_IN_TO_TWO_PLACES_IF_YOU_SUSPECT_ACCOUNT_THEFT_WE_RECOMMEND_CHANGING_YOUR_PASSWORD_SCANNING_YOUR_COMPUTER_FOR_VIRUSES_AND_USING_AN_ANTI_VIRUS_SOFTWARE));
}
sendLogout(account);
}
/**
* Gets the chars on server.
* @param account the account
*/
private void getCharsOnServer(String account)
{
int chars = 0;
final List<Long> charToDel = new ArrayList<>();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT deletetime FROM characters WHERE account_name=?"))
{
ps.setString(1, account);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
chars++;
final long delTime = rs.getLong("deletetime");
if (delTime != 0)
{
charToDel.add(delTime);
}
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Exception: getCharsOnServer: " + e.getMessage(), e);
}
final ReplyCharacters rec = new ReplyCharacters(account, chars, charToDel);
try
{
sendPacket(rec);
}
catch (IOException e)
{
}
}
/**
* Send packet.
* @param sl the sendable packet
* @throws IOException Signals that an I/O exception has occurred.
*/
private void sendPacket(BaseSendablePacket sl) throws IOException
{
final byte[] data = sl.getContent();
NewCrypt.appendChecksum(data);
_blowfish.crypt(data, 0, data.length);
final int len = data.length + 2;
synchronized (_out) // avoids tow threads writing in the mean time
{
_out.write(len & 0xff);
_out.write((len >> 8) & 0xff);
_out.write(data);
_out.flush();
}
}
/**
* Sets the max player.
* @param maxPlayer The maxPlayer to set.
*/
public void setMaxPlayer(int maxPlayer)
{
sendServerStatus(ServerStatus.MAX_PLAYERS, maxPlayer);
_maxPlayer = maxPlayer;
}
/**
* Gets the max player.
* @return Returns the maxPlayer.
*/
public int getMaxPlayer()
{
return _maxPlayer;
}
/**
* Send server status.
* @param id the id
* @param value the value
*/
public void sendServerStatus(int id, int value)
{
final ServerStatus ss = new ServerStatus();
ss.addAttribute(id, value);
try
{
sendPacket(ss);
}
catch (IOException e)
{
}
}
/**
* Send Server Type Config to LS.
*/
public void sendServerType()
{
final ServerStatus ss = new ServerStatus();
ss.addAttribute(ServerStatus.SERVER_TYPE, Config.SERVER_LIST_TYPE);
try
{
sendPacket(ss);
}
catch (IOException e)
{
}
}
/**
* Send change password.
* @param accountName the account name
* @param charName the char name
* @param oldpass the old pass
* @param newpass the new pass
*/
public void sendChangePassword(String accountName, String charName, String oldpass, String newpass)
{
final ChangePassword cp = new ChangePassword(accountName, charName, oldpass, newpass);
try
{
sendPacket(cp);
}
catch (IOException e)
{
}
}
/**
* Gets the status string.
* @return the status string
*/
public String getStatusString()
{
return ServerStatus.STATUS_STRING[_status];
}
/**
* Gets the server name.
* @return the server name.
*/
public String getServerName()
{
return _serverName;
}
/**
* Sets the server status.
* @param status the new server status
*/
public void setServerStatus(int status)
{
switch (status)
{
case ServerStatus.STATUS_AUTO:
{
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO);
_status = status;
break;
}
case ServerStatus.STATUS_DOWN:
{
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_DOWN);
_status = status;
break;
}
case ServerStatus.STATUS_FULL:
{
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_FULL);
_status = status;
break;
}
case ServerStatus.STATUS_GM_ONLY:
{
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY);
_status = status;
break;
}
case ServerStatus.STATUS_GOOD:
{
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GOOD);
_status = status;
break;
}
case ServerStatus.STATUS_NORMAL:
{
sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_NORMAL);
_status = status;
break;
}
default:
{
throw new IllegalArgumentException("Status does not exists:" + status);
}
}
}
public L2GameClient getClient(String name)
{
return name != null ? _accountsInGameServer.get(name) : null;
}
public static class SessionKey
{
public int playOkID1;
public int playOkID2;
public int loginOkID1;
public int loginOkID2;
/**
* Instantiates a new session key.
* @param loginOK1 the login o k1
* @param loginOK2 the login o k2
* @param playOK1 the play o k1
* @param playOK2 the play o k2
*/
public SessionKey(int loginOK1, int loginOK2, int playOK1, int playOK2)
{
playOkID1 = playOK1;
playOkID2 = playOK2;
loginOkID1 = loginOK1;
loginOkID2 = loginOK2;
}
@Override
public String toString()
{
return "PlayOk: " + playOkID1 + " " + playOkID2 + " LoginOk:" + loginOkID1 + " " + loginOkID2;
}
}
private static class WaitingClient
{
public String account;
public L2GameClient gameClient;
public SessionKey session;
/**
* Instantiates a new waiting client.
* @param acc the acc
* @param client the client
* @param key the key
*/
public WaitingClient(String acc, L2GameClient client, SessionKey key)
{
account = acc;
gameClient = client;
session = key;
}
}
private static class SingletonHolder
{
protected static final LoginServerThread _instance = new LoginServerThread();
}
}

View File

@@ -0,0 +1,141 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
public class MonsterRace
{
protected static final Logger LOGGER = Logger.getLogger(MonsterRace.class.getName());
private final L2Npc[] _monsters;
private int[][] _speeds;
private final int[] _first;
private final int[] _second;
protected MonsterRace()
{
_monsters = new L2Npc[8];
_speeds = new int[8][20];
_first = new int[2];
_second = new int[2];
}
public void newRace()
{
int random = 0;
for (int i = 0; i < 8; i++)
{
final int id = 31003;
random = Rnd.get(24);
while (true)
{
for (int j = i - 1; j >= 0; j--)
{
if (_monsters[j].getTemplate().getId() == (id + random))
{
random = Rnd.get(24);
continue;
}
}
break;
}
try
{
final L2NpcTemplate template = NpcData.getInstance().getTemplate(id + random);
_monsters[i] = (L2Npc) Class.forName("com.l2jmobius.gameserver.model.actor.instance." + template.getType() + "Instance").getConstructors()[0].newInstance(template);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Unable to create monster!", e);
}
}
newSpeeds();
}
public void newSpeeds()
{
_speeds = new int[8][20];
int total = 0;
_first[1] = 0;
_second[1] = 0;
for (int i = 0; i < 8; i++)
{
total = 0;
for (int j = 0; j < 20; j++)
{
_speeds[i][j] = j == 19 ? 100 : Rnd.get(60) + 65;
total += _speeds[i][j];
}
if (total >= _first[1])
{
_second[0] = _first[0];
_second[1] = _first[1];
_first[0] = 8 - i;
_first[1] = total;
}
else if (total >= _second[1])
{
_second[0] = 8 - i;
_second[1] = total;
}
}
}
/**
* @return Returns the monsters.
*/
public L2Npc[] getMonsters()
{
return _monsters;
}
/**
* @return Returns the speeds.
*/
public int[][] getSpeeds()
{
return _speeds;
}
public int getFirstPlace()
{
return _first[0];
}
public int getSecondPlace()
{
return _second[0];
}
public static MonsterRace getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final MonsterRace _instance = new MonsterRace();
}
}

View File

@@ -0,0 +1,605 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.database.DatabaseBackup;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.data.sql.impl.ClanTable;
import com.l2jmobius.gameserver.data.sql.impl.OfflineTradersTable;
import com.l2jmobius.gameserver.datatables.BotReportTable;
import com.l2jmobius.gameserver.instancemanager.CastleManorManager;
import com.l2jmobius.gameserver.instancemanager.CeremonyOfChaosManager;
import com.l2jmobius.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jmobius.gameserver.instancemanager.DBSpawnManager;
import com.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
import com.l2jmobius.gameserver.instancemanager.ItemAuctionManager;
import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jmobius.gameserver.instancemanager.QuestManager;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.entity.Hero;
import com.l2jmobius.gameserver.model.olympiad.Olympiad;
import com.l2jmobius.gameserver.network.ClientNetworkManager;
import com.l2jmobius.gameserver.network.Disconnection;
import com.l2jmobius.gameserver.network.EventLoopGroupManager;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.loginserverpackets.game.ServerStatus;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import com.l2jmobius.gameserver.network.telnet.TelnetServer;
import com.l2jmobius.gameserver.util.Broadcast;
/**
* This class provides the functions for shutting down and restarting the server.<br>
* It closes all open client connections and saves all data.
* @version $Revision: 1.2.4.5 $ $Date: 2005/03/27 15:29:09 $
*/
public class Shutdown extends Thread
{
private static final Logger LOGGER = Logger.getLogger(Shutdown.class.getName());
private static Shutdown _counterInstance = null;
private int _secondsShut;
private int _shutdownMode;
private static final int SIGTERM = 0;
private static final int GM_SHUTDOWN = 1;
private static final int GM_RESTART = 2;
private static final int ABORT = 3;
private static final String[] MODE_TEXT =
{
"SIGTERM",
"shutting down",
"restarting",
"aborting"
};
/**
* This function starts a shutdown count down from Telnet (Copied from Function startShutdown())
* @param seconds seconds until shutdown
*/
private void SendServerQuit(int seconds)
{
final SystemMessage sysm = SystemMessage.getSystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECOND_S_PLEASE_FIND_A_SAFE_PLACE_TO_LOG_OUT);
sysm.addInt(seconds);
Broadcast.toAllOnlinePlayers(sysm);
}
/**
* Default constructor is only used internal to create the shutdown-hook instance
*/
protected Shutdown()
{
_secondsShut = -1;
_shutdownMode = SIGTERM;
}
/**
* This creates a countdown instance of Shutdown.
* @param seconds how many seconds until shutdown
* @param restart true is the server shall restart after shutdown
*/
public Shutdown(int seconds, boolean restart)
{
if (seconds < 0)
{
seconds = 0;
}
_secondsShut = seconds;
_shutdownMode = restart ? GM_RESTART : GM_SHUTDOWN;
}
/**
* This function is called, when a new thread starts if this thread is the thread of getInstance, then this is the shutdown hook and we save all data and disconnect all clients.<br>
* After this thread ends, the server will completely exit if this is not the thread of getInstance, then this is a countdown thread.<br>
* We start the countdown, and when we finished it, and it was not aborted, we tell the shutdown-hook why we call exit, and then call exit when the exit status of the server is 1, startServer.sh / startServer.bat will restart the server.
*/
@Override
public void run()
{
if (this == getInstance())
{
final TimeCounter tc = new TimeCounter();
final TimeCounter tc1 = new TimeCounter();
try
{
if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS && !Config.STORE_OFFLINE_TRADE_IN_REALTIME)
{
OfflineTradersTable.getInstance().storeOffliners();
LOGGER.info("Offline Traders Table: Offline shops stored(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
}
catch (Throwable t)
{
LOGGER.log(Level.WARNING, "Error saving offline shops.", t);
}
try
{
disconnectAllCharacters();
LOGGER.info("All players disconnected and saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// ensure all services are stopped
try
{
GameTimeController.getInstance().stopTimer();
LOGGER.info("Game Time Controller: Timer stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// stop all thread pools
try
{
ThreadPool.shutdown();
LOGGER.info("Thread Pool Manager: Manager has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
try
{
LoginServerThread.getInstance().interrupt();
LOGGER.info("Login Server Thread: Thread interruped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
try
{
TelnetServer.getInstance().shutdown();
LOGGER.info("Telnet Server Thread: Thread interruped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// last byebye, save all data and quit this server
saveData();
tc.restartCounter();
// saveData sends messages to exit players, so shutdown selector after it
try
{
ClientNetworkManager.getInstance().stop();
EventLoopGroupManager.getInstance().shutdown();
LOGGER.info("Game Server: Selector thread has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
// ignore
}
// commit data, last chance
try
{
DatabaseFactory.close();
LOGGER.info("Database Factory: Database connection has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
catch (Throwable t)
{
}
// Backup database.
if (Config.BACKUP_DATABASE)
{
DatabaseBackup.performBackup();
}
// server will quit, when this function ends.
if (getInstance()._shutdownMode == GM_RESTART)
{
Runtime.getRuntime().halt(2);
}
else
{
Runtime.getRuntime().halt(0);
}
LOGGER.info("The server has been successfully shut down in " + (tc1.getEstimatedTime() / 1000) + "seconds.");
}
else
{
// gm shutdown: send warnings and then call exit to start shutdown sequence
countdown();
// last point where logging is operational :(
LOGGER.warning("GM shutdown countdown is over. " + MODE_TEXT[_shutdownMode] + " NOW!");
switch (_shutdownMode)
{
case GM_SHUTDOWN:
{
getInstance().setMode(GM_SHUTDOWN);
System.exit(0);
break;
}
case GM_RESTART:
{
getInstance().setMode(GM_RESTART);
System.exit(2);
break;
}
case ABORT:
{
LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_AUTO);
break;
}
}
}
}
/**
* This functions starts a shutdown countdown.
* @param activeChar GM who issued the shutdown command
* @param seconds seconds until shutdown
* @param restart true if the server will restart after shutdown
*/
public void startShutdown(L2PcInstance activeChar, int seconds, boolean restart)
{
_shutdownMode = restart ? GM_RESTART : GM_SHUTDOWN;
if (activeChar != null)
{
LOGGER.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
}
else
{
LOGGER.warning("Server scheduled restart issued shutdown command. Restart in " + seconds + " seconds!");
}
if (_shutdownMode > 0)
{
switch (seconds)
{
case 540:
case 480:
case 420:
case 360:
case 300:
case 240:
case 180:
case 120:
case 60:
case 30:
case 10:
case 5:
case 4:
case 3:
case 2:
case 1:
{
break;
}
default:
{
SendServerQuit(seconds);
}
}
}
if (_counterInstance != null)
{
_counterInstance._abort();
}
// the main instance should only run for shutdown hook, so we start a new instance
_counterInstance = new Shutdown(seconds, restart);
_counterInstance.start();
}
/**
* This function aborts a running countdown.
* @param activeChar GM who issued the abort command
*/
public void abort(L2PcInstance activeChar)
{
LOGGER.warning("GM: " + (activeChar != null ? activeChar.getName() + "(" + activeChar.getObjectId() + ") " : "") + "issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
if (_counterInstance != null)
{
_counterInstance._abort();
Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false);
}
}
/**
* Set the shutdown mode.
* @param mode what mode shall be set
*/
private void setMode(int mode)
{
_shutdownMode = mode;
}
/**
* Set shutdown mode to ABORT.
*/
private void _abort()
{
_shutdownMode = ABORT;
}
/**
* This counts the countdown and reports it to all players countdown is aborted if mode changes to ABORT.
*/
private void countdown()
{
try
{
while (_secondsShut > 0)
{
switch (_secondsShut)
{
case 540:
{
SendServerQuit(540);
break;
}
case 480:
{
SendServerQuit(480);
break;
}
case 420:
{
SendServerQuit(420);
break;
}
case 360:
{
SendServerQuit(360);
break;
}
case 300:
{
SendServerQuit(300);
break;
}
case 240:
{
SendServerQuit(240);
break;
}
case 180:
{
SendServerQuit(180);
break;
}
case 120:
{
SendServerQuit(120);
break;
}
case 60:
{
LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_DOWN); // avoids new players from logging in
SendServerQuit(60);
break;
}
case 30:
{
SendServerQuit(30);
break;
}
case 10:
{
SendServerQuit(10);
break;
}
case 5:
{
SendServerQuit(5);
break;
}
case 4:
{
SendServerQuit(4);
break;
}
case 3:
{
SendServerQuit(3);
break;
}
case 2:
{
SendServerQuit(2);
break;
}
case 1:
{
SendServerQuit(1);
break;
}
}
_secondsShut--;
final int delay = 1000; // milliseconds
Thread.sleep(delay);
if (_shutdownMode == ABORT)
{
break;
}
}
}
catch (InterruptedException e)
{
// this will never happen
}
}
/**
* This sends a last byebye, disconnects all players and saves data.
*/
private void saveData()
{
switch (_shutdownMode)
{
case SIGTERM:
{
LOGGER.info("SIGTERM received. Shutting down NOW!");
break;
}
case GM_SHUTDOWN:
{
LOGGER.info("GM shutdown received. Shutting down NOW!");
break;
}
case GM_RESTART:
{
LOGGER.info("GM restart received. Restarting NOW!");
break;
}
}
final TimeCounter tc = new TimeCounter();
// Save all raidboss and GrandBoss status ^_^
DBSpawnManager.getInstance().cleanUp();
LOGGER.info("RaidBossSpawnManager: All raidboss info saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
GrandBossManager.getInstance().cleanUp();
LOGGER.info("GrandBossManager: All Grand Boss info saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
ItemAuctionManager.getInstance().shutdown();
LOGGER.info("Item Auction Manager: All tasks stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
Olympiad.getInstance().saveOlympiadStatus();
LOGGER.info("Olympiad System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
CeremonyOfChaosManager.getInstance().stopScheduler();
LOGGER.info("CeremonyOfChaosManager: Scheduler stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
Hero.getInstance().shutdown();
LOGGER.info("Hero System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
ClanTable.getInstance().shutdown();
LOGGER.info("Clan System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save Cursed Weapons data before closing.
CursedWeaponsManager.getInstance().saveData();
LOGGER.info("Cursed Weapons Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save all manor data
if (!Config.ALT_MANOR_SAVE_ALL_ACTIONS)
{
CastleManorManager.getInstance().storeMe();
LOGGER.info("Castle Manor Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
// Save all global (non-player specific) Quest data that needs to persist after reboot
QuestManager.getInstance().save();
LOGGER.info("Quest Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save all global variables data
GlobalVariablesManager.getInstance().storeMe();
LOGGER.info("Global Variables Manager: Variables saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
// Save items on ground before closing
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().saveInDb();
LOGGER.info("Items On Ground Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
ItemsOnGroundManager.getInstance().cleanUp();
LOGGER.info("Items On Ground Manager: Cleaned up(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
}
// Save bot reports to database
if (Config.BOTREPORT_ENABLE)
{
BotReportTable.getInstance().saveReportedCharData();
LOGGER.info("Bot Report Table: Successfully saved reports to database!");
}
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
// never happens :p
}
}
/**
* This disconnects all clients from the server.
*/
private void disconnectAllCharacters()
{
for (L2PcInstance player : L2World.getInstance().getPlayers())
{
Disconnection.of(player).defaultSequence(true);
}
}
/**
* A simple class used to track down the estimated time of method executions.<br>
* Once this class is created, it saves the start time, and when you want to get the estimated time, use the getEstimatedTime() method.
*/
private static final class TimeCounter
{
private long _startTime;
protected TimeCounter()
{
restartCounter();
}
protected void restartCounter()
{
_startTime = System.currentTimeMillis();
}
protected long getEstimatedTimeAndRestartCounter()
{
final long toReturn = System.currentTimeMillis() - _startTime;
restartCounter();
return toReturn;
}
protected long getEstimatedTime()
{
return System.currentTimeMillis() - _startTime;
}
}
/**
* Get the shutdown-hook instance the shutdown-hook instance is created by the first call of this function, but it has to be registered externally.<br>
* @return instance of Shutdown, to be used as shutdown hook
*/
public static Shutdown getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final Shutdown _instance = new Shutdown();
}
}

View File

@@ -0,0 +1,811 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.interfaces.ILocational;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import com.l2jmobius.gameserver.network.serverpackets.AutoAttackStart;
import com.l2jmobius.gameserver.network.serverpackets.AutoAttackStop;
import com.l2jmobius.gameserver.network.serverpackets.Die;
import com.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
import com.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import com.l2jmobius.gameserver.network.serverpackets.StopMove;
import com.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
/**
* Mother class of all objects AI in the world.<br>
* AbastractAI :<br>
* <li>L2CharacterAI</li>
*/
public abstract class AbstractAI implements Ctrl
{
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
private NextAction _nextAction;
/**
* @return the _nextAction
*/
public NextAction getNextAction()
{
return _nextAction;
}
/**
* @param nextAction the next action to set.
*/
public void setNextAction(NextAction nextAction)
{
_nextAction = nextAction;
}
/** The character that this AI manages */
protected final L2Character _actor;
/** Current long-term intention */
protected CtrlIntention _intention = AI_INTENTION_IDLE;
/** Current long-term intention parameter */
protected Object[] _intentionArgs = null;
/** Flags about client's state, in order to know which messages to send */
protected volatile boolean _clientMoving;
/** Flags about client's state, in order to know which messages to send */
private volatile boolean _clientAutoAttacking;
/** Flags about client's state, in order to know which messages to send */
protected int _clientMovingToPawnOffset;
/** Different targets this AI maintains */
private L2Object _target;
/** The skill we are currently casting by INTENTION_CAST */
Skill _skill;
L2ItemInstance _item;
boolean _forceUse;
boolean _dontMove;
/** Different internal state flags */
protected int _moveToPawnTimeout;
private Future<?> _followTask = null;
private static final int FOLLOW_INTERVAL = 1000;
private static final int ATTACK_FOLLOW_INTERVAL = 500;
protected AbstractAI(L2Character creature)
{
_actor = creature;
}
/**
* @return the L2Character managed by this Accessor AI.
*/
@Override
public L2Character getActor()
{
return _actor;
}
/**
* @return the current Intention.
*/
@Override
public CtrlIntention getIntention()
{
return _intention;
}
/**
* Set the Intention of this AbstractAI.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method is USED by AI classes</B></FONT><B><U><br>
* Overridden in </U> : </B><BR>
* <B>L2AttackableAI</B> : Create an AI Task executed every 1s (if necessary)<BR>
* <B>L2PlayerAI</B> : Stores the current AI intention parameters to later restore it if necessary.
* @param intention The new Intention to set to the AI
* @param args The first parameter of the Intention
*/
synchronized void changeIntention(CtrlIntention intention, Object... args)
{
_intention = intention;
_intentionArgs = args;
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT>
* @param intention The new Intention to set to the AI
*/
@Override
public final void setIntention(CtrlIntention intention)
{
setIntention(intention, null, null);
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT>
* @param intention The new Intention to set to the AI
* @param args The first parameters of the Intention (optional target)
*/
@Override
@SafeVarargs
public final void setIntention(CtrlIntention intention, Object... args)
{
// Stop the follow mode if necessary
if ((intention != AI_INTENTION_FOLLOW) && (intention != AI_INTENTION_ATTACK))
{
stopFollow();
}
// Launch the onIntention method of the L2CharacterAI corresponding to the new Intention
switch (intention)
{
case AI_INTENTION_IDLE:
{
onIntentionIdle();
break;
}
case AI_INTENTION_ACTIVE:
{
onIntentionActive();
break;
}
case AI_INTENTION_REST:
{
onIntentionRest();
break;
}
case AI_INTENTION_ATTACK:
{
onIntentionAttack((L2Character) args[0]);
break;
}
case AI_INTENTION_CAST:
{
onIntentionCast((Skill) args[0], (L2Object) args[1], args.length > 2 ? (L2ItemInstance) args[2] : null, args.length > 3 ? (boolean) args[3] : false, args.length > 4 ? (boolean) args[4] : false);
break;
}
case AI_INTENTION_MOVE_TO:
{
onIntentionMoveTo((Location) args[0]);
break;
}
case AI_INTENTION_FOLLOW:
{
onIntentionFollow((L2Character) args[0]);
break;
}
case AI_INTENTION_PICK_UP:
{
onIntentionPickUp((L2Object) args[0]);
break;
}
case AI_INTENTION_INTERACT:
{
onIntentionInteract((L2Object) args[0]);
break;
}
}
// If do move or follow intention drop next action.
if ((_nextAction != null) && _nextAction.getIntentions().contains(intention))
{
_nextAction = null;
}
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
* @param evt The event whose the AI must be notified
*/
@Override
public final void notifyEvent(CtrlEvent evt)
{
notifyEvent(evt, null, null);
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event. <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
* @param evt The event whose the AI must be notified
* @param arg0 The first parameter of the Event (optional target)
*/
@Override
public final void notifyEvent(CtrlEvent evt, Object arg0)
{
notifyEvent(evt, arg0, null);
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event. <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
* @param evt The event whose the AI must be notified
* @param arg0 The first parameter of the Event (optional target)
* @param arg1 The second parameter of the Event (optional target)
*/
@Override
public final void notifyEvent(CtrlEvent evt, Object arg0, Object arg1)
{
if ((!_actor.isSpawned() && !_actor.isTeleporting()) || !_actor.hasAI())
{
return;
}
switch (evt)
{
case EVT_THINK:
{
onEvtThink();
break;
}
case EVT_ATTACKED:
{
onEvtAttacked((L2Character) arg0);
break;
}
case EVT_AGGRESSION:
{
onEvtAggression((L2Character) arg0, ((Number) arg1).intValue());
break;
}
case EVT_ACTION_BLOCKED:
{
onEvtActionBlocked((L2Character) arg0);
break;
}
case EVT_ROOTED:
{
onEvtRooted((L2Character) arg0);
break;
}
case EVT_CONFUSED:
{
onEvtConfused((L2Character) arg0);
break;
}
case EVT_MUTED:
{
onEvtMuted((L2Character) arg0);
break;
}
case EVT_EVADED:
{
onEvtEvaded((L2Character) arg0);
break;
}
case EVT_READY_TO_ACT:
{
if (!_actor.isCastingNow())
{
onEvtReadyToAct();
}
break;
}
case EVT_ARRIVED:
{
// happens e.g. from stopmove but we don't process it if we're casting
if (!_actor.isCastingNow())
{
onEvtArrived();
}
break;
}
case EVT_ARRIVED_REVALIDATE:
{
// this is disregarded if the char is not moving any more
if (_actor.isMoving())
{
onEvtArrivedRevalidate();
}
break;
}
case EVT_ARRIVED_BLOCKED:
{
onEvtArrivedBlocked((Location) arg0);
break;
}
case EVT_FORGET_OBJECT:
{
onEvtForgetObject((L2Object) arg0);
break;
}
case EVT_CANCEL:
{
onEvtCancel();
break;
}
case EVT_DEAD:
{
onEvtDead();
break;
}
case EVT_FAKE_DEATH:
{
onEvtFakeDeath();
break;
}
case EVT_FINISH_CASTING:
{
onEvtFinishCasting();
break;
}
}
// Do next action.
if ((_nextAction != null) && _nextAction.getEvents().contains(evt))
{
_nextAction.doAction();
}
}
protected abstract void onIntentionIdle();
protected abstract void onIntentionActive();
protected abstract void onIntentionRest();
protected abstract void onIntentionAttack(L2Character target);
protected abstract void onIntentionCast(Skill skill, L2Object target, L2ItemInstance item, boolean forceUse, boolean dontMove);
protected abstract void onIntentionMoveTo(Location destination);
protected abstract void onIntentionFollow(L2Character target);
protected abstract void onIntentionPickUp(L2Object item);
protected abstract void onIntentionInteract(L2Object object);
protected abstract void onEvtThink();
protected abstract void onEvtAttacked(L2Character attacker);
protected abstract void onEvtAggression(L2Character target, int aggro);
protected abstract void onEvtActionBlocked(L2Character attacker);
protected abstract void onEvtRooted(L2Character attacker);
protected abstract void onEvtConfused(L2Character attacker);
protected abstract void onEvtMuted(L2Character attacker);
protected abstract void onEvtEvaded(L2Character attacker);
protected abstract void onEvtReadyToAct();
protected abstract void onEvtArrived();
protected abstract void onEvtArrivedRevalidate();
protected abstract void onEvtArrivedBlocked(Location blocked_at_pos);
protected abstract void onEvtForgetObject(L2Object object);
protected abstract void onEvtCancel();
protected abstract void onEvtDead();
protected abstract void onEvtFakeDeath();
protected abstract void onEvtFinishCasting();
/**
* Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor. <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
protected void clientActionFailed()
{
if (_actor.isPlayer())
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
}
}
/**
* Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param pawn
* @param offset
*/
protected void moveToPawn(L2Object pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
{
if (offset < 10)
{
offset = 10;
}
// prevent possible extra calls to this function (there is none?),
// also don't send movetopawn packets too often
if (_clientMoving && (_target == pawn))
{
if (_clientMovingToPawnOffset == offset)
{
if (GameTimeController.getInstance().getGameTicks() < _moveToPawnTimeout)
{
return;
}
}
else if (_actor.isOnGeodataPath())
{
// minimum time to calculate new route is 2 seconds
if (GameTimeController.getInstance().getGameTicks() < (_moveToPawnTimeout + 10))
{
return;
}
}
}
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = offset;
_target = pawn;
_moveToPawnTimeout = GameTimeController.getInstance().getGameTicks();
_moveToPawnTimeout += 1000 / GameTimeController.MILLIS_IN_TICK;
if (pawn == null)
{
return;
}
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
_actor.moveToLocation(pawn.getX(), pawn.getY(), pawn.getZ(), offset);
if (!_actor.isMoving())
{
clientActionFailed();
return;
}
// Send a Server->Client packet MoveToPawn/CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
if (pawn.isCharacter())
{
if (_actor.isOnGeodataPath())
{
_actor.broadcastPacket(new MoveToLocation(_actor));
_clientMovingToPawnOffset = 0;
}
else
{
_actor.broadcastPacket(new MoveToPawn(_actor, pawn, offset));
}
}
else
{
_actor.broadcastPacket(new MoveToLocation(_actor));
}
}
else
{
clientActionFailed();
}
}
public void moveTo(ILocational loc)
{
moveTo(loc.getX(), loc.getY(), loc.getZ());
}
/**
* Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param x
* @param y
* @param z
*/
protected void moveTo(int x, int y, int z)
{
// Check if actor can move
if (!_actor.isMovementDisabled())
{
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = 0;
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
_actor.moveToLocation(x, y, z, 0);
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new MoveToLocation(_actor));
}
else
{
clientActionFailed();
}
}
/**
* Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param loc
*/
public void clientStopMoving(Location loc)
{
// Stop movement of the L2Character
if (_actor.isMoving())
{
_actor.stopMove(loc);
}
_clientMovingToPawnOffset = 0;
_clientMoving = false;
}
/**
* Client has already arrived to target, no need to force StopMove packet.
*/
protected void clientStoppedMoving()
{
if (_clientMovingToPawnOffset > 0) // movetoPawn needs to be stopped
{
_clientMovingToPawnOffset = 0;
_actor.broadcastPacket(new StopMove(_actor));
}
_clientMoving = false;
}
public boolean isAutoAttacking()
{
return _clientAutoAttacking;
}
public void setAutoAttacking(boolean isAutoAttacking)
{
if (_actor.isSummon())
{
final L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().setAutoAttacking(isAutoAttacking);
}
return;
}
_clientAutoAttacking = isAutoAttacking;
}
/**
* Start the actor Auto Attack client side by sending Server->Client packet AutoAttackStart <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
public void clientStartAutoAttack()
{
if (_actor.isSummon())
{
final L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().clientStartAutoAttack();
}
return;
}
if (!_clientAutoAttacking)
{
if (_actor.isPlayer() && _actor.hasSummon())
{
final L2Summon pet = _actor.getPet();
if (pet != null)
{
pet.broadcastPacket(new AutoAttackStart(pet.getObjectId()));
}
_actor.getServitors().values().forEach(s -> s.broadcastPacket(new AutoAttackStart(s.getObjectId())));
}
// Send a Server->Client packet AutoAttackStart to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new AutoAttackStart(_actor.getObjectId()));
setAutoAttacking(true);
}
AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor);
}
/**
* Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
void clientStopAutoAttack()
{
if (_actor.isSummon())
{
final L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().clientStopAutoAttack();
}
return;
}
if (_actor.isPlayer())
{
if (!AttackStanceTaskManager.getInstance().hasAttackStanceTask(_actor) && isAutoAttacking())
{
AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor);
}
}
else if (_clientAutoAttacking)
{
_actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId()));
setAutoAttacking(false);
}
}
/**
* Kill the actor client side by sending Server->Client packet AutoAttackStop, StopMove/StopRotation, Die <I>(broadcast)</I>.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
*/
protected void clientNotifyDead()
{
// Send a Server->Client packet Die to the actor and all L2PcInstance in its _knownPlayers
final Die msg = new Die(_actor);
_actor.broadcastPacket(msg);
// Init AI
_intention = AI_INTENTION_IDLE;
_target = null;
// Cancel the follow task if necessary
stopFollow();
}
/**
* Update the state of this actor client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance player.<br>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
* @param player The L2PcIstance to notify with state of this L2Character
*/
public void describeStateToPlayer(L2PcInstance player)
{
if (_actor.isVisibleFor(player))
{
if (_clientMoving)
{
if ((_clientMovingToPawnOffset != 0) && isFollowing())
{
// Send a Server->Client packet MoveToPawn to the actor and all L2PcInstance in its _knownPlayers
player.sendPacket(new MoveToPawn(_actor, _target, _clientMovingToPawnOffset));
}
else
{
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
player.sendPacket(new MoveToLocation(_actor));
}
}
}
}
public boolean isFollowing()
{
return (_target != null) && _target.isCharacter() && (_intention == AI_INTENTION_FOLLOW);
}
/**
* Create and Launch an AI Follow Task to execute every 1s.
* @param target The L2Character to follow
*/
public synchronized void startFollow(L2Character target)
{
startFollow(target, -1);
}
/**
* Create and Launch an AI Follow Task to execute every 0.5s, following at specified range.
* @param target The L2Character to follow
* @param range
*/
public synchronized void startFollow(L2Character target, int range)
{
if (_followTask != null)
{
_followTask.cancel(false);
_followTask = null;
}
setTarget(target);
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
_followTask = ThreadPool.scheduleAtFixedRate(() ->
{
try
{
if (_followTask == null)
{
return;
}
final L2Object followTarget = getTarget(); // copy to prevent NPE
if (followTarget == null)
{
if (_actor.isSummon())
{
((L2Summon) _actor).setFollowStatus(false);
}
setIntention(AI_INTENTION_IDLE);
return;
}
if (!_actor.isInsideRadius3D(followTarget, followRange))
{
if (!_actor.isInsideRadius3D(followTarget, 3000))
{
// if the target is too far (maybe also teleported)
if (_actor.isSummon())
{
((L2Summon) _actor).setFollowStatus(false);
}
setIntention(AI_INTENTION_IDLE);
return;
}
moveToPawn(followTarget, followRange);
}
}
catch (Exception e)
{
LOGGER.warning("Error: " + e.getMessage());
}
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
}
/**
* Stop an AI Follow Task.
*/
public synchronized void stopFollow()
{
if (_followTask != null)
{
// Stop the Follow Task
_followTask.cancel(false);
_followTask = null;
}
}
public void setTarget(L2Object target)
{
_target = target;
}
public L2Object getTarget()
{
return _target;
}
/**
* Stop all Ai tasks and futures.
*/
public void stopAITask()
{
stopFollow();
}
@Override
public String toString()
{
return "Actor: " + _actor;
}
}

View File

@@ -0,0 +1,90 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.gameserver.model.actor.L2Character;
/**
* Interface of AI and client state.<br>
* To correctly send messages to client we need it's state.<br>
* For example, if we've sent 'StartAutoAttack' message, we need to send 'StopAutoAttack' message before any other action.<br>
* Or if we've sent 'MoveToPawn', we need to send 'StopMove' when the movement of a character is canceled (by Root spell or any other reason).<br>
* Thus, we need to know the state of client, i.e. which messages we've sent and how the client will show the scene.<br>
* Close to this task is the task of AI.<br>
* If a player's character is attacking a mob, his ATTACK may be interrupted by an event, that temporary disable attacking.<br>
* But when the possibility to ATTACK will be enabled, the character must continue the ATTACK.<br>
* For mobs it may be more complex, since we want them to decide when to use magic, or when to follow the player for physical combat, or when to escape, to help another mob, etc.<br>
* This interface is hiding complexity of server<->client interaction and multiple states of a character.<br>
* It allows to set a desired, simple "wish" of a character, and the implementation of this interface will take care about the rest.<br>
* The goal of a character may be like "ATTACK", "random walk" and so on.<br>
* To reach the goal implementation will split it into several small actions, several steps (possibly repeatable).<br>
* Like "run to target" then "hit it", then if target is not dead - repeat.<br>
* This flow of simpler steps may be interrupted by incoming events.<br>
* Like a character's movement was disabled (by Root spell, for instance).<br>
* Depending on character's ability AI may choose to wait, or to use magic ATTACK and so on.<br>
* Additionally incoming events are compared with client's state of the character,<br>
* and required network messages are sent to client's, i.e. if we have incoming event that character's movement was disabled, it causes changing if its behavior,<br>
* and if client's state for the character is "moving" we send messages to clients to stop the avatar/mob.
*/
public interface Ctrl
{
/**
* Gets the actor.
* @return the actor
*/
L2Character getActor();
/**
* Gets the intention.
* @return the intention
*/
CtrlIntention getIntention();
/**
* Set general state/intention for AI, with optional data.
* @param intention the new intention
*/
void setIntention(CtrlIntention intention);
/**
* Sets the intention.
* @param intention the intention
* @param args
*/
void setIntention(CtrlIntention intention, Object... args);
/**
* Event, that notifies about previous step result, or user command, that does not change current general intention.
* @param evt the event
*/
void notifyEvent(CtrlEvent evt);
/**
* Notify an event.
* @param evt the event
* @param arg0 the arg0
*/
void notifyEvent(CtrlEvent evt, Object arg0);
/**
* Notify an event.
* @param evt the event
* @param arg0 the arg0
* @param arg1 the arg1
*/
void notifyEvent(CtrlEvent evt, Object arg0, Object arg1);
}

View File

@@ -0,0 +1,80 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
/**
* This class contains an enum of each possibles events that can happen on an AI character.
*/
public enum CtrlEvent
{
/**
* Something has changed, usually a previous step has being completed or maybe was completed, the AI must thing on next action.
*/
EVT_THINK,
/**
* The actor was attacked. This event comes each time a physical or magical<br>
* attack was done on the actor. NPC may start attack in response, or ignore<br>
* this event if they already attack someone, or change target and so on.
*/
EVT_ATTACKED,
/** Increase/decrease aggression towards a target, or reduce global aggression if target is null */
EVT_AGGRESSION,
/** Actor is in stun state */
EVT_ACTION_BLOCKED,
/** Actor is in rooted state (cannot move) */
EVT_ROOTED,
/** Actor evaded hit **/
EVT_EVADED,
/**
* An event that previous action was completed. The action may be an attempt to physically/magically hit an enemy, or an action that discarded attack attempt has finished.
*/
EVT_READY_TO_ACT,
/**
* The actor arrived to assigned location, or it's a time to modify movement destination (follow, interact, random move and others intentions).
*/
EVT_ARRIVED,
/**
* The actor arrived to an intermediate point, and needs to revalidate destination. This is sent when follow/move to pawn if destination is far away.
*/
EVT_ARRIVED_REVALIDATE,
/** The actor cannot move anymore. */
EVT_ARRIVED_BLOCKED,
/** Forgets an object (if it's used as attack target, follow target and so on */
EVT_FORGET_OBJECT,
/**
* Attempt to cancel current step execution, but not change the intention.<br>
* For example, the actor was put into a stun, so it's current attack<br>
* or movement has to be canceled. But after the stun state expired,<br>
* the actor may try to attack again. Another usage for CANCEL is a user's<br>
* attempt to cancel a cast/bow attack and so on.
*/
EVT_CANCEL,
/** The character is dead */
EVT_DEAD,
/** The character looks like dead */
EVT_FAKE_DEATH,
/** The character attack anyone randomly **/
EVT_CONFUSED,
/** The character cannot cast spells anymore **/
EVT_MUTED,
/** The character flee in random directions **/
EVT_AFRAID,
/** The character finish casting **/
EVT_FINISH_CASTING,
/** The character betrayed its master */
EVT_BETRAYED
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
/**
* Enumeration of generic intentions of an NPC/PC, an intention may require several steps to be completed.
*/
public enum CtrlIntention
{
/** Do nothing, disconnect AI of NPC if no players around */
AI_INTENTION_IDLE,
/** Alerted state without goal : scan attackable targets, random walk, etc */
AI_INTENTION_ACTIVE,
/** Rest (sit until attacked) */
AI_INTENTION_REST,
/** Attack target (cast combat magic, go to target, combat), may be ignored, if target is locked on another character or a peaceful zone and so on. */
AI_INTENTION_ATTACK,
/** Cast a spell, depending on the spell - may start or stop attacking */
AI_INTENTION_CAST,
/** Just move to another location */
AI_INTENTION_MOVE_TO,
/** Like move, but check target's movement and follow it */
AI_INTENTION_FOLLOW,
/** PickUp and item, (got to item, pickup it, become idle */
AI_INTENTION_PICK_UP,
/** Move to target, then interact */
AI_INTENTION_INTERACT;
}

View File

@@ -0,0 +1,281 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.DoppelgangerInstance;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillCaster;
import com.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
public class DoppelgangerAI extends L2CharacterAI
{
private volatile boolean _thinking; // to prevent recursive thinking
private volatile boolean _startFollow;
private L2Character _lastAttack = null;
public DoppelgangerAI(DoppelgangerInstance clone)
{
super(clone);
}
@Override
protected void onIntentionIdle()
{
stopFollow();
_startFollow = false;
onIntentionActive();
}
@Override
protected void onIntentionActive()
{
if (_startFollow)
{
setIntention(AI_INTENTION_FOLLOW, getActor().getSummoner());
}
else
{
super.onIntentionActive();
}
}
private void thinkAttack()
{
final L2Object target = getTarget();
final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
if (checkTargetLostOrDead(attackTarget))
{
setTarget(null);
return;
}
if (maybeMoveToPawn(target, _actor.getPhysicalAttackRange()))
{
return;
}
clientStopMoving(null);
_actor.doAutoAttack(attackTarget);
}
private void thinkCast()
{
if (_actor.isCastingNow(SkillCaster::isAnyNormalType))
{
return;
}
final L2Object target = _skill.getTarget(_actor, _forceUse, _dontMove, false);
if (checkTargetLost(target))
{
setTarget(null);
return;
}
final boolean val = _startFollow;
if (maybeMoveToPawn(target, _actor.getMagicalAttackRange(_skill)))
{
return;
}
getActor().followSummoner(false);
setIntention(AI_INTENTION_IDLE);
_startFollow = val;
_actor.doCast(_skill, _item, _forceUse, _dontMove);
}
private void thinkInteract()
{
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
}
@Override
protected void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
return;
}
_thinking = true;
try
{
switch (getIntention())
{
case AI_INTENTION_ATTACK:
{
thinkAttack();
break;
}
case AI_INTENTION_CAST:
{
thinkCast();
break;
}
case AI_INTENTION_INTERACT:
{
thinkInteract();
break;
}
}
}
finally
{
_thinking = false;
}
}
@Override
protected void onEvtFinishCasting()
{
if (_lastAttack == null)
{
getActor().followSummoner(_startFollow);
}
else
{
setIntention(AI_INTENTION_ATTACK, _lastAttack);
_lastAttack = null;
}
}
public void notifyFollowStatusChange()
{
_startFollow = !_startFollow;
switch (getIntention())
{
case AI_INTENTION_ACTIVE:
case AI_INTENTION_FOLLOW:
case AI_INTENTION_IDLE:
case AI_INTENTION_MOVE_TO:
case AI_INTENTION_PICK_UP:
{
getActor().followSummoner(_startFollow);
}
}
}
public void setStartFollowController(boolean val)
{
_startFollow = val;
}
@Override
protected void onIntentionCast(Skill skill, L2Object target, L2ItemInstance item, boolean forceUse, boolean dontMove)
{
if (getIntention() == AI_INTENTION_ATTACK)
{
_lastAttack = (getTarget() != null) && getTarget().isCharacter() ? (L2Character) getTarget() : null;
}
else
{
_lastAttack = null;
}
super.onIntentionCast(skill, target, item, forceUse, dontMove);
}
@Override
protected void moveToPawn(L2Object pawn, int offset)
{
// Check if actor can move
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
{
if (offset < 10)
{
offset = 10;
}
// prevent possible extra calls to this function (there is none?),
// also don't send movetopawn packets too often
boolean sendPacket = true;
if (_clientMoving && (getTarget() == pawn))
{
if (_clientMovingToPawnOffset == offset)
{
if (GameTimeController.getInstance().getGameTicks() < _moveToPawnTimeout)
{
return;
}
sendPacket = false;
}
else if (_actor.isOnGeodataPath())
{
// minimum time to calculate new route is 2 seconds
if (GameTimeController.getInstance().getGameTicks() < (_moveToPawnTimeout + 10))
{
return;
}
}
}
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = offset;
setTarget(pawn);
_moveToPawnTimeout = GameTimeController.getInstance().getGameTicks();
_moveToPawnTimeout += 1000 / GameTimeController.MILLIS_IN_TICK;
if (pawn == null)
{
return;
}
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
// _actor.moveToLocation(pawn.getX(), pawn.getY(), pawn.getZ(), offset);
final Location loc = new Location(pawn.getX() + Rnd.get(-offset, offset), pawn.getY() + Rnd.get(-offset, offset), pawn.getZ());
_actor.moveToLocation(loc.getX(), loc.getY(), loc.getZ(), 0);
if (!_actor.isMoving())
{
clientActionFailed();
return;
}
// Doppelgangers always send MoveToLocation packet.
if (sendPacket)
{
_actor.broadcastPacket(new MoveToLocation(_actor));
}
}
else
{
clientActionFailed();
}
}
@Override
public DoppelgangerInstance getActor()
{
return (DoppelgangerInstance) super.getActor();
}
}

View File

@@ -0,0 +1,239 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_REST;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
/**
* @author Sdw
*/
public class FriendlyNpcAI extends L2AttackableAI
{
public FriendlyNpcAI(L2Attackable attackable)
{
super(attackable);
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
}
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
}
@Override
protected void onIntentionAttack(L2Character target)
{
if (target == null)
{
clientActionFailed();
return;
}
if (getIntention() == AI_INTENTION_REST)
{
clientActionFailed();
return;
}
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow() || _actor.isControlBlocked())
{
clientActionFailed();
return;
}
// Set the Intention of this AbstractAI to AI_INTENTION_ATTACK
changeIntention(AI_INTENTION_ATTACK, target);
// Set the AI attack target
setTarget(target);
stopFollow();
// Launch the Think Event
notifyEvent(CtrlEvent.EVT_THINK, null);
}
@Override
protected void thinkAttack()
{
final L2Attackable npc = getActiveChar();
if (npc.isCastingNow() || npc.isCoreAIDisabled())
{
return;
}
final L2Object target = getTarget();
final L2Character originalAttackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
// Check if target is dead or if timeout is expired to stop this attack
if ((originalAttackTarget == null) || originalAttackTarget.isAlikeDead())
{
// Stop hating this target after the attack timeout or if target is dead
if (originalAttackTarget != null)
{
npc.stopHating(originalAttackTarget);
}
// Set the AI Intention to AI_INTENTION_ACTIVE
setIntention(AI_INTENTION_ACTIVE);
npc.setWalking();
return;
}
final int collision = npc.getTemplate().getCollisionRadius();
setTarget(originalAttackTarget);
final int combinedCollision = collision + originalAttackTarget.getTemplate().getCollisionRadius();
if (!npc.isMovementDisabled() && (Rnd.get(100) <= 3))
{
for (L2Attackable nearby : L2World.getInstance().getVisibleObjects(npc, L2Attackable.class))
{
if (npc.isInsideRadius2D(nearby, collision) && (nearby != originalAttackTarget))
{
int newX = combinedCollision + Rnd.get(40);
if (Rnd.nextBoolean())
{
newX += originalAttackTarget.getX();
}
else
{
newX = originalAttackTarget.getX() - newX;
}
int newY = combinedCollision + Rnd.get(40);
if (Rnd.nextBoolean())
{
newY += originalAttackTarget.getY();
}
else
{
newY = originalAttackTarget.getY() - newY;
}
if (!npc.isInsideRadius2D(newX, newY, 0, collision))
{
final int newZ = npc.getZ() + 30;
if (GeoEngine.getInstance().canMoveToTarget(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld()))
{
moveTo(newX, newY, newZ);
}
}
return;
}
}
}
// Dodge if its needed
if (!npc.isMovementDisabled() && (npc.getTemplate().getDodge() > 0))
{
if (Rnd.get(100) <= npc.getTemplate().getDodge())
{
final double distance2 = npc.calculateDistanceSq2D(originalAttackTarget);
if (Math.sqrt(distance2) <= (60 + combinedCollision))
{
int posX = npc.getX();
int posY = npc.getY();
final int posZ = npc.getZ() + 30;
if (originalAttackTarget.getX() < posX)
{
posX += 300;
}
else
{
posX -= 300;
}
if (originalAttackTarget.getY() < posY)
{
posY += 300;
}
else
{
posY -= 300;
}
if (GeoEngine.getInstance().canMoveToTarget(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()))
{
setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(posX, posY, posZ, 0));
}
return;
}
}
}
final double dist = npc.calculateDistance2D(originalAttackTarget);
final int dist2 = (int) dist - collision;
int range = npc.getPhysicalAttackRange() + combinedCollision;
if (originalAttackTarget.isMoving())
{
range += 50;
if (npc.isMoving())
{
range += 50;
}
}
if ((dist2 > range) || !GeoEngine.getInstance().canSeeTarget(npc, originalAttackTarget))
{
if (originalAttackTarget.isMoving())
{
range -= 100;
}
if (range < 5)
{
range = 5;
}
moveToPawn(originalAttackTarget, range);
return;
}
_actor.doAutoAttack(originalAttackTarget);
}
@Override
protected void thinkCast()
{
final L2Object target = _skill.getTarget(_actor, _forceUse, _dontMove, false);
if (checkTargetLost(target))
{
setTarget(null);
return;
}
if (maybeMoveToPawn(target, _actor.getMagicalAttackRange(_skill)))
{
return;
}
_actor.doCast(_skill, _item, _forceUse, _dontMove);
}
}

View File

@@ -0,0 +1,75 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.instance.L2AirShipInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.serverpackets.ExMoveToLocationAirShip;
import com.l2jmobius.gameserver.network.serverpackets.ExStopMoveAirShip;
/**
* @author DS
*/
public class L2AirShipAI extends L2VehicleAI
{
public L2AirShipAI(L2AirShipInstance airShip)
{
super(airShip);
}
@Override
protected void moveTo(int x, int y, int z)
{
if (!_actor.isMovementDisabled())
{
_clientMoving = true;
_actor.moveToLocation(x, y, z, 0);
_actor.broadcastPacket(new ExMoveToLocationAirShip(getActor()));
}
}
@Override
public void clientStopMoving(Location loc)
{
if (_actor.isMoving())
{
_actor.stopMove(loc);
}
if (_clientMoving || (loc != null))
{
_clientMoving = false;
_actor.broadcastPacket(new ExStopMoveAirShip(getActor()));
}
}
@Override
public void describeStateToPlayer(L2PcInstance player)
{
if (_clientMoving)
{
player.sendPacket(new ExMoveToLocationAirShip(getActor()));
}
}
@Override
public L2AirShipInstance getActor()
{
return (L2AirShipInstance) _actor;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.instance.L2BoatInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.serverpackets.VehicleDeparture;
import com.l2jmobius.gameserver.network.serverpackets.VehicleInfo;
import com.l2jmobius.gameserver.network.serverpackets.VehicleStarted;
/**
* @author DS
*/
public class L2BoatAI extends L2VehicleAI
{
public L2BoatAI(L2BoatInstance boat)
{
super(boat);
}
@Override
protected void moveTo(int x, int y, int z)
{
if (!_actor.isMovementDisabled())
{
if (!_clientMoving)
{
_actor.broadcastPacket(new VehicleStarted(getActor(), 1));
}
_clientMoving = true;
_actor.moveToLocation(x, y, z, 0);
_actor.broadcastPacket(new VehicleDeparture(getActor()));
}
}
@Override
public void clientStopMoving(Location loc)
{
if (_actor.isMoving())
{
_actor.stopMove(loc);
}
if (_clientMoving || (loc != null))
{
_clientMoving = false;
_actor.broadcastPacket(new VehicleStarted(getActor(), 0));
_actor.broadcastPacket(new VehicleInfo(getActor()));
}
}
@Override
public void describeStateToPlayer(L2PcInstance player)
{
if (_clientMoving)
{
player.sendPacket(new VehicleDeparture(getActor()));
}
}
@Override
public L2BoatInstance getActor()
{
return (L2BoatInstance) _actor;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,545 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import java.util.ArrayList;
import java.util.List;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.MobGroup;
import com.l2jmobius.gameserver.model.MobGroupTable;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Playable;
import com.l2jmobius.gameserver.model.actor.instance.L2ControllableMobInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.util.Util;
/**
* AI for controllable mobs
* @author littlecrow
*/
public final class L2ControllableMobAI extends L2AttackableAI
{
public static final int AI_IDLE = 1;
public static final int AI_NORMAL = 2;
public static final int AI_FORCEATTACK = 3;
public static final int AI_FOLLOW = 4;
public static final int AI_CAST = 5;
public static final int AI_ATTACK_GROUP = 6;
private int _alternateAI;
private boolean _isThinking; // to prevent thinking recursively
private boolean _isNotMoving;
private L2Character _forcedTarget;
private MobGroup _targetGroup;
protected void thinkFollow()
{
final L2Attackable me = (L2Attackable) _actor;
if (!Util.checkIfInRange(MobGroupTable.FOLLOW_RANGE, me, getForcedTarget(), true))
{
final int signX = Rnd.nextBoolean() ? -1 : 1;
final int signY = Rnd.nextBoolean() ? -1 : 1;
final int randX = Rnd.get(MobGroupTable.FOLLOW_RANGE);
final int randY = Rnd.get(MobGroupTable.FOLLOW_RANGE);
moveTo(getForcedTarget().getX() + (signX * randX), getForcedTarget().getY() + (signY * randY), getForcedTarget().getZ());
}
}
@Override
protected void onEvtThink()
{
if (_isThinking)
{
return;
}
setThinking(true);
try
{
switch (_alternateAI)
{
case AI_IDLE:
{
if (getIntention() != AI_INTENTION_ACTIVE)
{
setIntention(AI_INTENTION_ACTIVE);
}
break;
}
case AI_FOLLOW:
{
thinkFollow();
break;
}
case AI_CAST:
{
thinkCast();
break;
}
case AI_FORCEATTACK:
{
thinkForceAttack();
break;
}
case AI_ATTACK_GROUP:
{
thinkAttackGroup();
break;
}
default:
{
if (getIntention() == AI_INTENTION_ACTIVE)
{
thinkActive();
}
else if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
break;
}
}
}
finally
{
setThinking(false);
}
}
@Override
protected void thinkCast()
{
L2Object target = _skill.getTarget(_actor, _forceUse, _dontMove, false);
if ((target == null) || !target.isCharacter() || ((L2Character) target).isAlikeDead())
{
target = _skill.getTarget(_actor, findNextRndTarget(), _forceUse, _dontMove, false);
}
if (target == null)
{
return;
}
setTarget(target);
if (!_actor.isMuted())
{
int max_range = 0;
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
if (Util.checkIfInRange(sk.getCastRange(), _actor, target, true) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_actor.doCast(sk);
return;
}
max_range = Math.max(max_range, sk.getCastRange());
}
if (!_isNotMoving)
{
moveToPawn(target, max_range);
}
return;
}
}
protected void thinkAttackGroup()
{
final L2Character target = getForcedTarget();
if ((target == null) || target.isAlikeDead())
{
// try to get next group target
setForcedTarget(findNextGroupTarget());
clientStopMoving(null);
}
if (target == null)
{
return;
}
setTarget(target);
// as a response, we put the target in a forcedattack mode
final L2ControllableMobInstance theTarget = (L2ControllableMobInstance) target;
final L2ControllableMobAI ctrlAi = (L2ControllableMobAI) theTarget.getAI();
ctrlAi.forceAttack(_actor);
final double dist2 = _actor.calculateDistanceSq2D(target);
final int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + target.getTemplate().getCollisionRadius();
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_actor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
if (!_isNotMoving)
{
moveToPawn(target, range);
}
return;
}
_actor.doAutoAttack(target);
}
protected void thinkForceAttack()
{
if ((getForcedTarget() == null) || getForcedTarget().isAlikeDead())
{
clientStopMoving(null);
setIntention(AI_INTENTION_ACTIVE);
setAlternateAI(AI_IDLE);
}
setTarget(getForcedTarget());
final double dist2 = _actor.calculateDistanceSq2D(getForcedTarget());
final int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + getForcedTarget().getTemplate().getCollisionRadius();
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_actor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
if (!_isNotMoving)
{
moveToPawn(getForcedTarget(), _actor.getPhysicalAttackRange()/* range */);
}
return;
}
_actor.doAutoAttack(getForcedTarget());
}
@Override
protected void thinkAttack()
{
L2Character target = getForcedTarget();
if ((target == null) || target.isAlikeDead())
{
if (target != null)
{
// stop hating
final L2Attackable npc = (L2Attackable) _actor;
npc.stopHating(target);
}
setIntention(AI_INTENTION_ACTIVE);
}
else
{
// notify aggression
final L2Character finalTarget = target;
if (((L2Npc) _actor).getTemplate().getClans() != null)
{
L2World.getInstance().forEachVisibleObject(_actor, L2Npc.class, npc ->
{
if (!npc.isInMyClan((L2Npc) _actor))
{
return;
}
if (_actor.isInsideRadius3D(npc, npc.getTemplate().getClanHelpRange()))
{
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, finalTarget, 1);
}
});
}
setTarget(target);
final double dist2 = _actor.calculateDistanceSq2D(target);
final int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + target.getTemplate().getCollisionRadius();
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (Skill sk : _actor.getAllSkills())
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_actor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
moveToPawn(target, range);
return;
}
// Force mobs to attack anybody if confused.
L2Character hated;
if (_actor.isConfused())
{
hated = findNextRndTarget();
}
else
{
hated = target;
}
if (hated == null)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
if (hated != target)
{
target = hated;
}
if (!_actor.isMuted() && (Rnd.get(5) == 3))
{
for (Skill sk : _actor.getAllSkills())
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)))
{
_actor.doCast(sk);
return;
}
}
}
_actor.doAutoAttack(target);
}
}
@Override
protected void thinkActive()
{
L2Character hated;
if (_actor.isConfused())
{
hated = findNextRndTarget();
}
else
{
final L2Object target = _actor.getTarget();
hated = (target != null) && target.isCharacter() ? (L2Character) target : null;
}
if (hated != null)
{
_actor.setRunning();
setIntention(AI_INTENTION_ATTACK, hated);
}
}
private boolean checkAutoAttackCondition(L2Character target)
{
if ((target == null) || !_actor.isAttackable())
{
return false;
}
final L2Attackable me = (L2Attackable) _actor;
if (target.isNpc() || target.isDoor())
{
return false;
}
if (target.isAlikeDead() || !me.isInsideRadius2D(target, me.getAggroRange()) || (Math.abs(_actor.getZ() - target.getZ()) > 100))
{
return false;
}
// Check if the target isn't invulnerable
if (target.isInvul())
{
return false;
}
// Spawn protection (only against mobs)
if (target.isPlayer() && ((L2PcInstance) target).isSpawnProtected())
{
return false;
}
// Check if the target is a L2Playable
if (target.isPlayable())
{
// Check if the target isn't in silent move mode
if (((L2Playable) target).isSilentMovingAffected())
{
return false;
}
}
if (target.isNpc())
{
return false;
}
return me.isAggressive();
}
private L2Character findNextRndTarget()
{
final List<L2Character> potentialTarget = new ArrayList<>();
L2World.getInstance().forEachVisibleObject(_actor, L2Character.class, target ->
{
if (Util.checkIfInShortRange(((L2Attackable) _actor).getAggroRange(), _actor, target, true) && checkAutoAttackCondition(target))
{
potentialTarget.add(target);
}
});
return !potentialTarget.isEmpty() ? potentialTarget.get(Rnd.get(potentialTarget.size())) : null;
}
private L2ControllableMobInstance findNextGroupTarget()
{
return getGroupTarget().getRandomMob();
}
public L2ControllableMobAI(L2ControllableMobInstance controllableMob)
{
super(controllableMob);
setAlternateAI(AI_IDLE);
}
public int getAlternateAI()
{
return _alternateAI;
}
public void setAlternateAI(int _alternateai)
{
_alternateAI = _alternateai;
}
public void forceAttack(L2Character target)
{
setAlternateAI(AI_FORCEATTACK);
setForcedTarget(target);
}
public void forceAttackGroup(MobGroup group)
{
setForcedTarget(null);
setGroupTarget(group);
setAlternateAI(AI_ATTACK_GROUP);
}
public void stop()
{
setAlternateAI(AI_IDLE);
clientStopMoving(null);
}
public void move(int x, int y, int z)
{
moveTo(x, y, z);
}
public void follow(L2Character target)
{
setAlternateAI(AI_FOLLOW);
setForcedTarget(target);
}
public boolean isThinking()
{
return _isThinking;
}
public boolean isNotMoving()
{
return _isNotMoving;
}
public void setNotMoving(boolean isNotMoving)
{
_isNotMoving = isNotMoving;
}
public void setThinking(boolean isThinking)
{
_isThinking = isThinking;
}
private L2Character getForcedTarget()
{
return _forcedTarget;
}
private MobGroup getGroupTarget()
{
return _targetGroup;
}
private void setForcedTarget(L2Character forcedTarget)
{
_forcedTarget = forcedTarget;
}
private void setGroupTarget(MobGroup targetGroup)
{
_targetGroup = targetGroup;
}
}

View File

@@ -0,0 +1,168 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* @author mkizub
*/
public class L2DoorAI extends L2CharacterAI
{
public L2DoorAI(L2DoorInstance door)
{
super(door);
}
@Override
protected void onIntentionIdle()
{
}
@Override
protected void onIntentionActive()
{
}
@Override
protected void onIntentionRest()
{
}
@Override
protected void onIntentionAttack(L2Character target)
{
}
@Override
protected void onIntentionCast(Skill skill, L2Object target, L2ItemInstance item, boolean forceUse, boolean dontMove)
{
}
@Override
protected void onIntentionMoveTo(Location destination)
{
}
@Override
protected void onIntentionFollow(L2Character target)
{
}
@Override
protected void onIntentionPickUp(L2Object item)
{
}
@Override
protected void onIntentionInteract(L2Object object)
{
}
@Override
protected void onEvtThink()
{
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
ThreadPool.execute(new onEventAttackedDoorTask((L2DoorInstance) _actor, attacker));
}
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
}
@Override
protected void onEvtActionBlocked(L2Character attacker)
{
}
@Override
protected void onEvtRooted(L2Character attacker)
{
}
@Override
protected void onEvtReadyToAct()
{
}
@Override
protected void onEvtArrived()
{
}
@Override
protected void onEvtArrivedRevalidate()
{
}
@Override
protected void onEvtArrivedBlocked(Location blocked_at_loc)
{
}
@Override
protected void onEvtForgetObject(L2Object object)
{
}
@Override
protected void onEvtCancel()
{
}
@Override
protected void onEvtDead()
{
}
private class onEventAttackedDoorTask implements Runnable
{
private final L2DoorInstance _door;
private final L2Character _attacker;
public onEventAttackedDoorTask(L2DoorInstance door, L2Character attacker)
{
_door = door;
_attacker = attacker;
}
@Override
public void run()
{
L2World.getInstance().forEachVisibleObject(_door, L2DefenderInstance.class, guard ->
{
if (_actor.isInsideRadius3D(guard, guard.getTemplate().getClanHelpRange()))
{
guard.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attacker, 15);
}
});
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Playable;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.zone.ZoneId;
import com.l2jmobius.gameserver.network.SystemMessageId;
/**
* This class manages AI of L2Playable.<br>
* L2PlayableAI :
* <li>L2SummonAI</li>
* <li>L2PlayerAI</li>
* @author JIV
*/
public abstract class L2PlayableAI extends L2CharacterAI
{
public L2PlayableAI(L2Playable playable)
{
super(playable);
}
@Override
protected void onIntentionAttack(L2Character target)
{
if ((target != null) && target.isPlayable())
{
if (target.getActingPlayer().isProtectionBlessingAffected() && ((_actor.getActingPlayer().getLevel() - target.getActingPlayer().getLevel()) >= 10) && (_actor.getActingPlayer().getReputation() < 0) && !(target.isInsideZone(ZoneId.PVP)))
{
// If attacker have karma and have level >= 10 than his target and target have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (_actor.getActingPlayer().isProtectionBlessingAffected() && ((target.getActingPlayer().getLevel() - _actor.getActingPlayer().getLevel()) >= 10) && (target.getActingPlayer().getReputation() < 0) && !(target.isInsideZone(ZoneId.PVP)))
{
// If target have karma and have level >= 10 than his target and actor have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (target.getActingPlayer().isCursedWeaponEquipped() && (_actor.getActingPlayer().getLevel() <= 20))
{
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (_actor.getActingPlayer().isCursedWeaponEquipped() && (target.getActingPlayer().getLevel() <= 20))
{
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
}
super.onIntentionAttack(target);
}
@Override
protected void onIntentionCast(Skill skill, L2Object target, L2ItemInstance item, boolean forceUse, boolean dontMove)
{
if ((target != null) && (target.isPlayable()) && skill.isBad())
{
if (target.getActingPlayer().isProtectionBlessingAffected() && ((_actor.getActingPlayer().getLevel() - target.getActingPlayer().getLevel()) >= 10) && (_actor.getActingPlayer().getReputation() < 0) && !target.isInsideZone(ZoneId.PVP))
{
// If attacker have karma and have level >= 10 than his target and target have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (_actor.getActingPlayer().isProtectionBlessingAffected() && ((target.getActingPlayer().getLevel() - _actor.getActingPlayer().getLevel()) >= 10) && (target.getActingPlayer().getReputation() < 0) && !target.isInsideZone(ZoneId.PVP))
{
// If target have karma and have level >= 10 than his target and actor have
// Newbie Protection Buff,
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
if (target.getActingPlayer().isCursedWeaponEquipped() && ((_actor.getActingPlayer().getLevel() <= 20) || (target.getActingPlayer().getLevel() <= 20)))
{
_actor.getActingPlayer().sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
clientActionFailed();
return;
}
}
super.onIntentionCast(skill, target, item, forceUse, dontMove);
}
}

View File

@@ -0,0 +1,379 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_CAST;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_MOVE_TO;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_REST;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2StaticObjectInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.targets.TargetType;
public class L2PlayerAI extends L2PlayableAI
{
private boolean _thinking; // to prevent recursive thinking
private IntentionCommand _nextIntention = null;
public L2PlayerAI(L2PcInstance player)
{
super(player);
}
private void saveNextIntention(CtrlIntention intention, Object arg0, Object arg1)
{
_nextIntention = new IntentionCommand(intention, arg0, arg1);
}
@Override
public IntentionCommand getNextIntention()
{
return _nextIntention;
}
/**
* Saves the current Intention for this L2PlayerAI if necessary and calls changeIntention in AbstractAI.
* @param intention The new Intention to set to the AI
* @param args The first parameter of the Intention
*/
@Override
protected synchronized void changeIntention(CtrlIntention intention, Object... args)
{
final Object localArg0 = args.length > 0 ? args[0] : null;
final Object localArg1 = args.length > 1 ? args[1] : null;
final Object globalArg0 = (_intentionArgs != null) && (_intentionArgs.length > 0) ? _intentionArgs[0] : null;
final Object globalArg1 = (_intentionArgs != null) && (_intentionArgs.length > 1) ? _intentionArgs[1] : null;
// do nothing unless CAST intention
// however, forget interrupted actions when starting to use an offensive skill
if ((intention != AI_INTENTION_CAST) || ((Skill) args[0]).isBad())
{
_nextIntention = null;
super.changeIntention(intention, args);
return;
}
// do nothing if next intention is same as current one.
if ((intention == _intention) && (globalArg0 == localArg0) && (globalArg1 == localArg1))
{
super.changeIntention(intention, args);
return;
}
// save current intention so it can be used after cast
saveNextIntention(_intention, globalArg0, globalArg1);
super.changeIntention(intention, args);
}
/**
* Launch actions corresponding to the Event ReadyToAct.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Launch actions corresponding to the Event Think</li>
* </ul>
*/
@Override
protected void onEvtReadyToAct()
{
// Launch actions corresponding to the Event Think
if (_nextIntention != null)
{
setIntention(_nextIntention._crtlIntention, _nextIntention._arg0, _nextIntention._arg1);
_nextIntention = null;
}
super.onEvtReadyToAct();
}
/**
* Launch actions corresponding to the Event Cancel.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Stop an AI Follow Task</li>
* <li>Launch actions corresponding to the Event Think</li>
* </ul>
*/
@Override
protected void onEvtCancel()
{
_nextIntention = null;
super.onEvtCancel();
}
/**
* Finalize the casting of a skill. This method overrides L2CharacterAI method.<br>
* <B>What it does:</B><br>
* Check if actual intention is set to CAST and, if so, retrieves latest intention before the actual CAST and set it as the current intention for the player.
*/
@Override
protected void onEvtFinishCasting()
{
if (getIntention() == AI_INTENTION_CAST)
{
// run interrupted or next intention
final IntentionCommand nextIntention = _nextIntention;
if (nextIntention != null)
{
if (nextIntention._crtlIntention != AI_INTENTION_CAST) // previous state shouldn't be casting
{
setIntention(nextIntention._crtlIntention, nextIntention._arg0, nextIntention._arg1);
}
else
{
setIntention(AI_INTENTION_IDLE);
}
}
else
{
// set intention to idle if skill doesn't change intention.
setIntention(AI_INTENTION_IDLE);
}
}
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
super.onEvtAttacked(attacker);
// Summons in defending mode defend its master when attacked.
if (_actor.getActingPlayer().hasServitors())
{
_actor.getActingPlayer().getServitors().values().stream().filter(summon -> ((L2SummonAI) summon.getAI()).isDefending()).forEach(summon -> ((L2SummonAI) summon.getAI()).defendAttack(attacker));
}
}
@Override
protected void onEvtEvaded(L2Character attacker)
{
super.onEvtEvaded(attacker);
// Summons in defending mode defend its master when attacked.
if (_actor.getActingPlayer().hasServitors())
{
_actor.getActingPlayer().getServitors().values().stream().filter(summon -> ((L2SummonAI) summon.getAI()).isDefending()).forEach(summon -> ((L2SummonAI) summon.getAI()).defendAttack(attacker));
}
}
@Override
protected void onIntentionRest()
{
if (getIntention() != AI_INTENTION_REST)
{
changeIntention(AI_INTENTION_REST);
setTarget(null);
clientStopMoving(null);
}
}
@Override
protected void onIntentionActive()
{
setIntention(AI_INTENTION_IDLE);
}
/**
* Manage the Move To Intention : Stop current Attack and Launch a Move to Location Task.<br>
* <B><U> Actions</U> : </B>
* <ul>
* <li>Stop the actor auto-attack server side AND client side by sending Server->Client packet AutoAttackStop (broadcast)</li>
* <li>Set the Intention of this AI to AI_INTENTION_MOVE_TO</li>
* <li>Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast)</li>
* </ul>
*/
@Override
protected void onIntentionMoveTo(Location loc)
{
if (getIntention() == AI_INTENTION_REST)
{
// Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor
clientActionFailed();
return;
}
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow() || _actor.isAttackingNow())
{
clientActionFailed();
saveNextIntention(AI_INTENTION_MOVE_TO, loc, null);
return;
}
// Set the Intention of this AbstractAI to AI_INTENTION_MOVE_TO
changeIntention(AI_INTENTION_MOVE_TO, loc);
// Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
clientStopAutoAttack();
// Abort the attack of the L2Character and send Server->Client ActionFailed packet
_actor.abortAttack();
// Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast)
moveTo(loc.getX(), loc.getY(), loc.getZ());
}
@Override
protected void clientNotifyDead()
{
_clientMovingToPawnOffset = 0;
_clientMoving = false;
super.clientNotifyDead();
}
private void thinkAttack()
{
final L2Object target = getTarget();
if ((target == null) || !target.isCharacter())
{
return;
}
if (checkTargetLostOrDead((L2Character) target))
{
// Notify the target
setTarget(null);
return;
}
if (maybeMoveToPawn(target, _actor.getPhysicalAttackRange()))
{
return;
}
clientStopMoving(null);
_actor.doAutoAttack((L2Character) target);
}
private void thinkCast()
{
final L2Object target = _skill.getTarget(_actor, _forceUse, _dontMove, false);
if ((_skill.getTargetType() == TargetType.GROUND) && _actor.isPlayer())
{
if (maybeMoveToPosition(((L2PcInstance) _actor).getCurrentSkillWorldPosition(), _actor.getMagicalAttackRange(_skill)))
{
return;
}
}
else
{
if (checkTargetLost(target))
{
if (_skill.isBad() && (target != null))
{
// Notify the target
setTarget(null);
}
return;
}
if ((target != null) && maybeMoveToPawn(target, _actor.getMagicalAttackRange(_skill)))
{
return;
}
}
_actor.doCast(_skill, _item, _forceUse, _dontMove);
}
private void thinkPickUp()
{
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow())
{
return;
}
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
getActor().doPickupItem(target);
}
private void thinkInteract()
{
if (_actor.isAllSkillsDisabled() || _actor.isCastingNow())
{
return;
}
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
if (!(target instanceof L2StaticObjectInstance))
{
getActor().doInteract((L2Character) target);
}
setIntention(AI_INTENTION_IDLE);
}
@Override
protected void onEvtThink()
{
if (_thinking && (getIntention() != AI_INTENTION_CAST))
{
return;
}
_thinking = true;
try
{
if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
else if (getIntention() == AI_INTENTION_CAST)
{
thinkCast();
}
else if (getIntention() == AI_INTENTION_PICK_UP)
{
thinkPickUp();
}
else if (getIntention() == AI_INTENTION_INTERACT)
{
thinkInteract();
}
}
finally
{
_thinking = false;
}
}
@Override
public L2PcInstance getActor()
{
return (L2PcInstance) super.getActor();
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.gameserver.model.actor.instance.L2ShuttleInstance;
import com.l2jmobius.gameserver.network.serverpackets.shuttle.ExShuttleMove;
/**
* @author UnAfraid
*/
public class L2ShuttleAI extends L2VehicleAI
{
public L2ShuttleAI(L2ShuttleInstance shuttle)
{
super(shuttle);
}
@Override
public void moveTo(int x, int y, int z)
{
if (!_actor.isMovementDisabled())
{
_clientMoving = true;
_actor.moveToLocation(x, y, z, 0);
_actor.broadcastPacket(new ExShuttleMove(getActor(), x, y, z));
}
}
@Override
public L2ShuttleInstance getActor()
{
return (L2ShuttleInstance) _actor;
}
}

View File

@@ -0,0 +1,386 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.concurrent.Future;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.geoengine.GeoEngine;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillCaster;
public class L2SummonAI extends L2PlayableAI implements Runnable
{
private static final int AVOID_RADIUS = 70;
private volatile boolean _thinking; // to prevent recursive thinking
private volatile boolean _startFollow = ((L2Summon) _actor).getFollowStatus();
private L2Character _lastAttack = null;
private volatile boolean _startAvoid;
private volatile boolean _isDefending;
private Future<?> _avoidTask = null;
public L2SummonAI(L2Summon summon)
{
super(summon);
}
@Override
protected void onIntentionIdle()
{
stopFollow();
_startFollow = false;
onIntentionActive();
}
@Override
protected void onIntentionActive()
{
final L2Summon summon = (L2Summon) _actor;
if (_startFollow)
{
setIntention(AI_INTENTION_FOLLOW, summon.getOwner());
}
else
{
super.onIntentionActive();
}
}
@Override
synchronized void changeIntention(CtrlIntention intention, Object... args)
{
switch (intention)
{
case AI_INTENTION_ACTIVE:
case AI_INTENTION_FOLLOW:
{
startAvoidTask();
break;
}
default:
{
stopAvoidTask();
}
}
super.changeIntention(intention, args);
}
private void thinkAttack()
{
final L2Object target = getTarget();
final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
if (checkTargetLostOrDead(attackTarget))
{
setTarget(null);
((L2Summon) _actor).setFollowStatus(true);
return;
}
if (maybeMoveToPawn(attackTarget, _actor.getPhysicalAttackRange()))
{
return;
}
clientStopMoving(null);
_actor.doAutoAttack(attackTarget);
}
private void thinkCast()
{
final L2Summon summon = (L2Summon) _actor;
if (summon.isCastingNow(SkillCaster::isAnyNormalType))
{
return;
}
final L2Object target = _skill.getTarget(_actor, _skill.isBad(), _dontMove, false);
if (checkTargetLost(target))
{
setTarget(null);
summon.setFollowStatus(true);
return;
}
final boolean val = _startFollow;
if (maybeMoveToPawn(target, _actor.getMagicalAttackRange(_skill)))
{
return;
}
summon.setFollowStatus(false);
setIntention(AI_INTENTION_IDLE);
_startFollow = val;
_actor.doCast(_skill, _item, _skill.isBad(), _dontMove);
}
private void thinkPickUp()
{
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
getActor().doPickupItem(target);
}
private void thinkInteract()
{
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
}
@Override
protected void onEvtThink()
{
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
{
return;
}
_thinking = true;
try
{
switch (getIntention())
{
case AI_INTENTION_ATTACK:
{
thinkAttack();
break;
}
case AI_INTENTION_CAST:
{
thinkCast();
break;
}
case AI_INTENTION_PICK_UP:
{
thinkPickUp();
break;
}
case AI_INTENTION_INTERACT:
{
thinkInteract();
break;
}
}
}
finally
{
_thinking = false;
}
}
@Override
protected void onEvtFinishCasting()
{
if (_lastAttack == null)
{
((L2Summon) _actor).setFollowStatus(_startFollow);
}
else
{
setIntention(AI_INTENTION_ATTACK, _lastAttack);
_lastAttack = null;
}
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
super.onEvtAttacked(attacker);
if (_isDefending)
{
defendAttack(attacker);
}
else
{
avoidAttack(attacker);
}
}
@Override
protected void onEvtEvaded(L2Character attacker)
{
super.onEvtEvaded(attacker);
if (_isDefending)
{
defendAttack(attacker);
}
else
{
avoidAttack(attacker);
}
}
private void avoidAttack(L2Character attacker)
{
// Don't move while casting. It breaks casting animation, but still casts the skill... looks so bugged.
if (_actor.isCastingNow())
{
return;
}
final L2Character owner = getActor().getOwner();
// trying to avoid if summon near owner
if ((owner != null) && (owner != attacker) && owner.isInsideRadius3D(_actor, 2 * AVOID_RADIUS))
{
_startAvoid = true;
}
}
public void defendAttack(L2Character attacker)
{
// Cannot defend while attacking or casting.
if (_actor.isAttackingNow() || _actor.isCastingNow())
{
return;
}
final L2Summon summon = getActor();
if ((summon.getOwner() != null) && (summon.getOwner() != attacker) && !summon.isMoving() && summon.canAttack(attacker, false) && summon.getOwner().isInsideRadius3D(_actor, 2 * AVOID_RADIUS))
{
summon.doAutoAttack(attacker);
}
}
@Override
public void run()
{
if (_startAvoid)
{
_startAvoid = false;
if (!_clientMoving && !_actor.isDead() && !_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
{
final int ownerX = ((L2Summon) _actor).getOwner().getX();
final int ownerY = ((L2Summon) _actor).getOwner().getY();
final double angle = Math.toRadians(Rnd.get(-90, 90)) + Math.atan2(ownerY - _actor.getY(), ownerX - _actor.getX());
final int targetX = ownerX + (int) (AVOID_RADIUS * Math.cos(angle));
final int targetY = ownerY + (int) (AVOID_RADIUS * Math.sin(angle));
if (GeoEngine.getInstance().canMoveToTarget(_actor.getX(), _actor.getY(), _actor.getZ(), targetX, targetY, _actor.getZ(), _actor.getInstanceWorld()))
{
moveTo(targetX, targetY, _actor.getZ());
}
}
}
}
public void notifyFollowStatusChange()
{
_startFollow = !_startFollow;
switch (getIntention())
{
case AI_INTENTION_ACTIVE:
case AI_INTENTION_FOLLOW:
case AI_INTENTION_IDLE:
case AI_INTENTION_MOVE_TO:
case AI_INTENTION_PICK_UP:
{
((L2Summon) _actor).setFollowStatus(_startFollow);
}
}
}
public void setStartFollowController(boolean val)
{
_startFollow = val;
}
@Override
protected void onIntentionCast(Skill skill, L2Object target, L2ItemInstance item, boolean forceUse, boolean dontMove)
{
if (getIntention() == AI_INTENTION_ATTACK)
{
_lastAttack = (getTarget() != null) && getTarget().isCharacter() ? (L2Character) getTarget() : null;
}
else
{
_lastAttack = null;
}
super.onIntentionCast(skill, target, item, forceUse, dontMove);
}
private void startAvoidTask()
{
if (_avoidTask == null)
{
_avoidTask = ThreadPool.scheduleAtFixedRate(this, 100, 100);
}
}
private void stopAvoidTask()
{
if (_avoidTask != null)
{
_avoidTask.cancel(false);
_avoidTask = null;
}
}
@Override
public void stopAITask()
{
stopAvoidTask();
super.stopAITask();
}
@Override
public L2Summon getActor()
{
return (L2Summon) super.getActor();
}
/**
* @return if the summon is defending itself or master.
*/
public boolean isDefending()
{
return _isDefending;
}
/**
* @param isDefending set the summon to defend itself and master, or be passive and avoid while being attacked.
*/
public void setDefending(boolean isDefending)
{
_isDefending = isDefending;
}
}

View File

@@ -0,0 +1,123 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Vehicle;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* @author DS
*/
public abstract class L2VehicleAI extends L2CharacterAI
{
/**
* Simple AI for vehicles
* @param vehicle
*/
public L2VehicleAI(L2Vehicle vehicle)
{
super(vehicle);
}
@Override
protected void onIntentionAttack(L2Character target)
{
}
@Override
protected void onIntentionCast(Skill skill, L2Object target, L2ItemInstance item, boolean forceUse, boolean dontMove)
{
}
@Override
protected void onIntentionFollow(L2Character target)
{
}
@Override
protected void onIntentionPickUp(L2Object item)
{
}
@Override
protected void onIntentionInteract(L2Object object)
{
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
}
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
}
@Override
protected void onEvtActionBlocked(L2Character attacker)
{
}
@Override
protected void onEvtRooted(L2Character attacker)
{
}
@Override
protected void onEvtForgetObject(L2Object object)
{
}
@Override
protected void onEvtCancel()
{
}
@Override
protected void onEvtDead()
{
}
@Override
protected void onEvtFakeDeath()
{
}
@Override
protected void onEvtFinishCasting()
{
}
@Override
protected void clientActionFailed()
{
}
@Override
protected void moveToPawn(L2Object pawn, int offset)
{
}
@Override
protected void clientStoppedMoving()
{
}
}

View File

@@ -0,0 +1,205 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.ai;
import java.util.ArrayList;
import java.util.List;
/**
* Class for AI action after some event.<br>
* Has 2 array list for "work" and "break".
* @author Yaroslav
*/
public class NextAction
{
public interface NextActionCallback
{
void doWork();
}
private List<CtrlEvent> _events;
private List<CtrlIntention> _intentions;
private NextActionCallback _callback;
/**
* Main constructor.
* @param events
* @param intentions
* @param callback
*/
public NextAction(List<CtrlEvent> events, List<CtrlIntention> intentions, NextActionCallback callback)
{
_events = events;
_intentions = intentions;
setCallback(callback);
}
/**
* Single constructor.
* @param event
* @param intention
* @param callback
*/
public NextAction(CtrlEvent event, CtrlIntention intention, NextActionCallback callback)
{
if (_events == null)
{
_events = new ArrayList<>();
}
if (_intentions == null)
{
_intentions = new ArrayList<>();
}
if (event != null)
{
_events.add(event);
}
if (intention != null)
{
_intentions.add(intention);
}
setCallback(callback);
}
/**
* Do action.
*/
public void doAction()
{
if (_callback != null)
{
_callback.doWork();
}
}
/**
* @return the _event
*/
public List<CtrlEvent> getEvents()
{
// If null return empty list.
if (_events == null)
{
_events = new ArrayList<>();
}
return _events;
}
/**
* @param event the event to set.
*/
public void setEvents(ArrayList<CtrlEvent> event)
{
_events = event;
}
/**
* @param event
*/
public void addEvent(CtrlEvent event)
{
if (_events == null)
{
_events = new ArrayList<>();
}
if (event != null)
{
_events.add(event);
}
}
/**
* @param event
*/
public void removeEvent(CtrlEvent event)
{
if (_events == null)
{
return;
}
_events.remove(event);
}
/**
* @return the _callback
*/
public NextActionCallback getCallback()
{
return _callback;
}
/**
* @param callback the callback to set.
*/
public void setCallback(NextActionCallback callback)
{
_callback = callback;
}
/**
* @return the _intentions
*/
public List<CtrlIntention> getIntentions()
{
// If null return empty list.
if (_intentions == null)
{
_intentions = new ArrayList<>();
}
return _intentions;
}
/**
* @param intentions the intention to set.
*/
public void setIntentions(ArrayList<CtrlIntention> intentions)
{
_intentions = intentions;
}
/**
* @param intention
*/
public void addIntention(CtrlIntention intention)
{
if (_intentions == null)
{
_intentions = new ArrayList<>();
}
if (intention != null)
{
_intentions.add(intention);
}
}
/**
* @param intention
*/
public void removeIntention(CtrlIntention intention)
{
if (_intentions == null)
{
return;
}
_intentions.remove(intention);
}
}

View File

@@ -0,0 +1,292 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.cache;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.file.filter.HTMLFilter;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.util.BuilderUtil;
import com.l2jmobius.gameserver.util.Util;
/**
* @author Layane
*/
public class HtmCache
{
private static final Logger LOGGER = Logger.getLogger(HtmCache.class.getName());
private static final HTMLFilter HTML_FILTER = new HTMLFilter();
private static final Pattern EXTEND_PATTERN = Pattern.compile("<extend template=\"([a-zA-Z0-9-_./\\ ]*)\">(.*?)</extend>", Pattern.DOTALL);
private static final Pattern ABSTRACT_BLOCK_PATTERN = Pattern.compile("<abstract block=\"([a-zA-Z0-9-_. ]*)\" ?/>", Pattern.DOTALL);
private static final Pattern BLOCK_PATTERN = Pattern.compile("<block name=\"([a-zA-Z0-9-_. ]*)\">(.*?)</block>", Pattern.DOTALL);
private static final Map<String, String> _cache = Config.LAZY_CACHE ? new ConcurrentHashMap<>() : new HashMap<>();
private int _loadedFiles;
private long _bytesBuffLen;
protected HtmCache()
{
reload();
}
public void reload()
{
reload(Config.DATAPACK_ROOT);
}
public void reload(File f)
{
if (!Config.LAZY_CACHE)
{
LOGGER.info("Html cache start...");
parseDir(f);
LOGGER.info("Cache[HTML]: " + String.format("%.3f", getMemoryUsage()) + " megabytes on " + _loadedFiles + " files loaded");
}
else
{
_cache.clear();
_loadedFiles = 0;
_bytesBuffLen = 0;
LOGGER.info("Cache[HTML]: Running lazy cache");
}
}
public void reloadPath(File f)
{
parseDir(f);
LOGGER.info("Cache[HTML]: Reloaded specified path.");
}
public double getMemoryUsage()
{
return (float) _bytesBuffLen / 1048576;
}
public int getLoadedFiles()
{
return _loadedFiles;
}
private void parseDir(File dir)
{
final File[] files = dir.listFiles();
if (files != null)
{
for (File file : files)
{
if (!file.isDirectory())
{
loadFile(file);
}
else
{
parseDir(file);
}
}
}
}
public String loadFile(File file)
{
if (HTML_FILTER.accept(file))
{
try
{
String content = processHtml(Util.readAllLines(file, StandardCharsets.UTF_8, null));
content = content.replaceAll("(?s)<!--.*?-->", ""); // Remove html comments
// content = content.replaceAll("\r", "").replaceAll("\n", ""); // Remove new lines
final String oldContent = _cache.put(file.toURI().getPath().substring(Config.DATAPACK_ROOT.toURI().getPath().length()), content);
if (oldContent == null)
{
_bytesBuffLen += content.length() * 2;
_loadedFiles++;
}
else
{
_bytesBuffLen = (_bytesBuffLen - oldContent.length()) + (content.length() * 2);
}
return content;
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Problem with htm file:", e);
}
}
return null;
}
public String getHtmForce(L2PcInstance player, String path)
{
String content = getHtm(player, path);
if (content == null)
{
content = "<html><body>My text is missing:<br>" + path + "</body></html>";
LOGGER.warning("Cache[HTML]: Missing HTML page: " + path);
}
return content;
}
public String getHtm(L2PcInstance player, String path)
{
final String prefix = player != null ? player.getHtmlPrefix() : "en";
String newPath = null;
String content;
if ((prefix != null) && !prefix.isEmpty())
{
newPath = prefix + path;
content = getHtm(newPath);
if (content != null)
{
if ((player != null) && player.isGM() && Config.GM_DEBUG_HTML_PATHS)
{
BuilderUtil.sendHtmlMessage(player, newPath.substring(5));
}
return content;
}
}
content = getHtm(path);
if ((content != null) && (newPath != null))
{
_cache.put(newPath, content);
}
if ((player != null) && player.isGM() && (path != null) && Config.GM_DEBUG_HTML_PATHS)
{
BuilderUtil.sendHtmlMessage(player, path.substring(5));
}
return content;
}
private String getHtm(String path)
{
// TODO: Check why some files do not get in cache on server startup.
return (path == null) || path.isEmpty() ? "" : _cache.get(path) == null ? loadFile(new File(Config.DATAPACK_ROOT, path)) : _cache.get(path);
}
public boolean contains(String path)
{
return _cache.containsKey(path);
}
/**
* @param path The path to the HTM
* @return {@code true} if the path targets a HTM or HTML file, {@code false} otherwise.
*/
public boolean isLoadable(String path)
{
return HTML_FILTER.accept(new File(Config.DATAPACK_ROOT, path));
}
private String parseTemplateName(String name)
{
if (!name.startsWith("data/"))
{
if (name.startsWith("html/"))
{
return "data/" + name;
}
else if (name.startsWith("CommunityBoard/"))
{
return "data/html/" + name;
}
else if (name.startsWith("scripts/"))
{
return "data/scripts/" + name;
}
}
return name;
}
private String processHtml(String result)
{
final Matcher extendMatcher = EXTEND_PATTERN.matcher(result);
if (extendMatcher.find())
{
// If extend matcher finds something, process template
final String templateName = parseTemplateName(extendMatcher.group(1));
// Generate block name -> content map
final Map<String, String> blockMap = generateBlockMap(result);
// Attempt to find the template
String template = getHtm(templateName + "-template.htm");
if (template != null)
{
// Attempt to find the abstract blocks
final Matcher blockMatcher = ABSTRACT_BLOCK_PATTERN.matcher(template);
while (blockMatcher.find())
{
final String name = blockMatcher.group(1);
if (!blockMap.containsKey(name))
{
LOGGER.warning(getClass().getSimpleName() + ": Abstract block definition [" + name + "] is not implemented!");
continue;
}
// Replace the matched content with the block.
template = template.replace(blockMatcher.group(0), blockMap.get(name));
}
// Replace the entire extend block
result = result.replace(extendMatcher.group(0), template);
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Missing template: " + templateName + "-template.htm !");
}
}
return result;
}
private Map<String, String> generateBlockMap(String data)
{
final Map<String, String> blockMap = new LinkedHashMap<>();
final Matcher blockMatcher = BLOCK_PATTERN.matcher(data);
while (blockMatcher.find())
{
final String name = blockMatcher.group(1);
final String content = blockMatcher.group(2);
blockMap.put(name, content);
}
return blockMap;
}
public static HtmCache getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final HtmCache _instance = new HtmCache();
}
}

View File

@@ -0,0 +1,79 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author -Nemesiss-
*/
public class WarehouseCacheManager
{
final Map<L2PcInstance, Long> _cachedWh = new ConcurrentHashMap<>();
final long _cacheTime = Config.WAREHOUSE_CACHE_TIME * 60000;
protected WarehouseCacheManager()
{
ThreadPool.scheduleAtFixedRate(new CacheScheduler(), 120000, 60000);
}
public void addCacheTask(L2PcInstance pc)
{
_cachedWh.put(pc, System.currentTimeMillis());
}
public void remCacheTask(L2PcInstance pc)
{
_cachedWh.remove(pc);
}
private class CacheScheduler implements Runnable
{
public CacheScheduler()
{
}
@Override
public void run()
{
final long cTime = System.currentTimeMillis();
for (L2PcInstance pc : _cachedWh.keySet())
{
if ((cTime - _cachedWh.get(pc)) > _cacheTime)
{
pc.clearWarehouse();
_cachedWh.remove(pc);
}
}
}
}
public static WarehouseCacheManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final WarehouseCacheManager _instance = new WarehouseCacheManager();
}
}

View File

@@ -0,0 +1,247 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.TopicBBSManager;
public class Forum
{
private static final Logger LOGGER = Logger.getLogger(Forum.class.getName());
// type
public static final int ROOT = 0;
public static final int NORMAL = 1;
public static final int CLAN = 2;
public static final int MEMO = 3;
public static final int MAIL = 4;
// perm
public static final int INVISIBLE = 0;
public static final int ALL = 1;
public static final int CLANMEMBERONLY = 2;
public static final int OWNERONLY = 3;
private final List<Forum> _children;
private final Map<Integer, Topic> _topic = new ConcurrentHashMap<>();
private final int _forumId;
private String _forumName;
private int _forumType;
private int _forumPost;
private int _forumPerm;
private final Forum _fParent;
private int _ownerID;
private boolean _loaded = false;
/**
* Creates new instance of Forum. When you create new forum, use {@link com.l2jmobius.gameserver.communitybbs.Manager.ForumsBBSManager# addForum(com.l2jmobius.gameserver.communitybbs.BB.Forum)} to add forum to the forums manager.
* @param Forumid
* @param FParent
*/
public Forum(int Forumid, Forum FParent)
{
_forumId = Forumid;
_fParent = FParent;
_children = new CopyOnWriteArrayList<>();
}
/**
* @param name
* @param parent
* @param type
* @param perm
* @param OwnerID
*/
public Forum(String name, Forum parent, int type, int perm, int OwnerID)
{
_forumName = name;
_forumId = ForumsBBSManager.getInstance().getANewID();
_forumType = type;
_forumPost = 0;
_forumPerm = perm;
_fParent = parent;
_ownerID = OwnerID;
_children = new CopyOnWriteArrayList<>();
parent._children.add(this);
ForumsBBSManager.getInstance().addForum(this);
_loaded = true;
}
private void load()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM forums WHERE forum_id=?"))
{
ps.setInt(1, _forumId);
try (ResultSet rs = ps.executeQuery())
{
if (rs.next())
{
_forumName = rs.getString("forum_name");
_forumPost = rs.getInt("forum_post");
_forumType = rs.getInt("forum_type");
_forumPerm = rs.getInt("forum_perm");
_ownerID = rs.getInt("forum_owner_id");
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Data error on Forum " + _forumId + " : " + e.getMessage(), e);
}
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM topic WHERE topic_forum_id=? ORDER BY topic_id DESC"))
{
ps.setInt(1, _forumId);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
final Topic t = new Topic(Topic.ConstructorType.RESTORE, rs.getInt("topic_id"), rs.getInt("topic_forum_id"), rs.getString("topic_name"), rs.getLong("topic_date"), rs.getString("topic_ownername"), rs.getInt("topic_ownerid"), rs.getInt("topic_type"), rs.getInt("topic_reply"));
_topic.put(t.getID(), t);
if (t.getID() > TopicBBSManager.getInstance().getMaxID(this))
{
TopicBBSManager.getInstance().setMaxID(t.getID(), this);
}
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Data error on Forum " + _forumId + " : " + e.getMessage(), e);
}
}
private void getChildren()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT forum_id FROM forums WHERE forum_parent=?"))
{
ps.setInt(1, _forumId);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
final Forum f = new Forum(rs.getInt("forum_id"), this);
_children.add(f);
ForumsBBSManager.getInstance().addForum(f);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Data error on Forum (children): " + e.getMessage(), e);
}
}
public int getTopicSize()
{
vload();
return _topic.size();
}
public Topic getTopic(int j)
{
vload();
return _topic.get(j);
}
public void addTopic(Topic t)
{
vload();
_topic.put(t.getID(), t);
}
/**
* @return the forum Id
*/
public int getID()
{
return _forumId;
}
public String getName()
{
vload();
return _forumName;
}
public int getType()
{
vload();
return _forumType;
}
/**
* @param name the forum name
* @return the forum for the given name
*/
public Forum getChildByName(String name)
{
vload();
return _children.stream().filter(f -> f.getName().equals(name)).findFirst().orElse(null);
}
/**
* @param id
*/
public void rmTopicByID(int id)
{
_topic.remove(id);
}
public void insertIntoDb()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO forums (forum_id,forum_name,forum_parent,forum_post,forum_type,forum_perm,forum_owner_id) VALUES (?,?,?,?,?,?,?)"))
{
ps.setInt(1, _forumId);
ps.setString(2, _forumName);
ps.setInt(3, _fParent.getID());
ps.setInt(4, _forumPost);
ps.setInt(5, _forumType);
ps.setInt(6, _forumPerm);
ps.setInt(7, _ownerID);
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Error while saving new Forum to db " + e.getMessage(), e);
}
}
public void vload()
{
if (!_loaded)
{
load();
getChildren();
_loaded = true;
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.PostBBSManager;
/**
* @author Maktakien
*/
public class Post
{
private static Logger LOGGER = Logger.getLogger(Post.class.getName());
public static class CPost
{
public int postId;
public String postOwner;
public int postOwnerId;
public long postDate;
public int postTopicId;
public int postForumId;
public String postTxt;
}
private final List<CPost> _post;
/**
* @param _PostOwner
* @param _PostOwnerID
* @param date
* @param tid
* @param _PostForumID
* @param txt
*/
public Post(String _PostOwner, int _PostOwnerID, long date, int tid, int _PostForumID, String txt)
{
_post = new CopyOnWriteArrayList<>();
final CPost cp = new CPost();
cp.postId = 0;
cp.postOwner = _PostOwner;
cp.postOwnerId = _PostOwnerID;
cp.postDate = date;
cp.postTopicId = tid;
cp.postForumId = _PostForumID;
cp.postTxt = txt;
_post.add(cp);
insertindb(cp);
}
private void insertindb(CPost cp)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO posts (post_id,post_owner_name,post_ownerid,post_date,post_topic_id,post_forum_id,post_txt) values (?,?,?,?,?,?,?)"))
{
ps.setInt(1, cp.postId);
ps.setString(2, cp.postOwner);
ps.setInt(3, cp.postOwnerId);
ps.setLong(4, cp.postDate);
ps.setInt(5, cp.postTopicId);
ps.setInt(6, cp.postForumId);
ps.setString(7, cp.postTxt);
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Error while saving new Post to db " + e.getMessage(), e);
}
}
public Post(Topic t)
{
_post = new CopyOnWriteArrayList<>();
load(t);
}
public CPost getCPost(int id)
{
int i = 0;
for (CPost cp : _post)
{
if (i++ == id)
{
return cp;
}
}
return null;
}
public void deleteme(Topic t)
{
PostBBSManager.getInstance().delPostByTopic(t);
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM posts WHERE post_forum_id=? AND post_topic_id=?"))
{
ps.setInt(1, t.getForumID());
ps.setInt(2, t.getID());
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Error while deleting post: " + e.getMessage(), e);
}
}
/**
* @param t
*/
private void load(Topic t)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM posts WHERE post_forum_id=? AND post_topic_id=? ORDER BY post_id ASC"))
{
ps.setInt(1, t.getForumID());
ps.setInt(2, t.getID());
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
final CPost cp = new CPost();
cp.postId = rs.getInt("post_id");
cp.postOwner = rs.getString("post_owner_name");
cp.postOwnerId = rs.getInt("post_ownerid");
cp.postDate = rs.getLong("post_date");
cp.postTopicId = rs.getInt("post_topic_id");
cp.postForumId = rs.getInt("post_forum_id");
cp.postTxt = rs.getString("post_txt");
_post.add(cp);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Data error on Post " + t.getForumID() + "/" + t.getID() + " : " + e.getMessage(), e);
}
}
/**
* @param i
*/
public void updatetxt(int i)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE posts SET post_txt=? WHERE post_id=? AND post_topic_id=? AND post_forum_id=?"))
{
final CPost cp = getCPost(i);
ps.setString(1, cp.postTxt);
ps.setInt(2, cp.postId);
ps.setInt(3, cp.postTopicId);
ps.setInt(4, cp.postForumId);
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Error while saving new Post to db " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,152 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.TopicBBSManager;
public class Topic
{
private static final Logger LOGGER = Logger.getLogger(Topic.class.getName());
public static final int MORMAL = 0;
public static final int MEMO = 1;
private final int _id;
private final int _forumId;
private final String _topicName;
private final long _date;
private final String _ownerName;
private final int _ownerId;
private final int _type;
private final int _cReply;
/**
* @param ct
* @param id
* @param fid
* @param name
* @param date
* @param oname
* @param oid
* @param type
* @param Creply
*/
public Topic(ConstructorType ct, int id, int fid, String name, long date, String oname, int oid, int type, int Creply)
{
_id = id;
_forumId = fid;
_topicName = name;
_date = date;
_ownerName = oname;
_ownerId = oid;
_type = type;
_cReply = Creply;
TopicBBSManager.getInstance().addTopic(this);
if (ct == ConstructorType.CREATE)
{
insertindb();
}
}
private void insertindb()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO topic (topic_id,topic_forum_id,topic_name,topic_date,topic_ownername,topic_ownerid,topic_type,topic_reply) values (?,?,?,?,?,?,?,?)"))
{
ps.setInt(1, _id);
ps.setInt(2, _forumId);
ps.setString(3, _topicName);
ps.setLong(4, _date);
ps.setString(5, _ownerName);
ps.setInt(6, _ownerId);
ps.setInt(7, _type);
ps.setInt(8, _cReply);
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Error while saving new Topic to db " + e.getMessage(), e);
}
}
public enum ConstructorType
{
RESTORE,
CREATE
}
/**
* @return the topic Id
*/
public int getID()
{
return _id;
}
public int getForumID()
{
return _forumId;
}
/**
* @return the topic name
*/
public String getName()
{
return _topicName;
}
public String getOwnerName()
{
return _ownerName;
}
/**
* @param f
*/
public void deleteme(Forum f)
{
TopicBBSManager.getInstance().delTopic(this);
f.rmTopicByID(_id);
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM topic WHERE topic_id=? AND topic_forum_id=?"))
{
ps.setInt(1, _id);
ps.setInt(2, f.getID());
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Error while deleting topic: " + e.getMessage(), e);
}
}
/**
* @return the topic date
*/
public long getDate()
{
return _date;
}
}

View File

@@ -0,0 +1,79 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.util.ArrayList;
import java.util.List;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.serverpackets.ShowBoard;
public abstract class BaseBBSManager
{
public abstract void parsecmd(String command, L2PcInstance activeChar);
public abstract void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar);
/**
* @param html
* @param acha
*/
protected void send1001(String html, L2PcInstance acha)
{
if (html.length() < 8192)
{
acha.sendPacket(new ShowBoard(html, "1001"));
}
}
/**
* @param acha
*/
protected void send1002(L2PcInstance acha)
{
send1002(acha, " ", " ", "0");
}
/**
* @param activeChar
* @param string
* @param string2
* @param string3
*/
protected void send1002(L2PcInstance activeChar, String string, String string2, String string3)
{
final List<String> _arg = new ArrayList<>(20);
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add(activeChar.getName());
_arg.add(Integer.toString(activeChar.getObjectId()));
_arg.add(activeChar.getAccountName());
_arg.add("9");
_arg.add(string2); // subject?
_arg.add(string2); // subject?
_arg.add(string); // text
_arg.add(string3); // date?
_arg.add(string3); // date?
_arg.add("0");
_arg.add("0");
activeChar.sendPacket(new ShowBoard(_arg));
}
}

View File

@@ -0,0 +1,168 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.BB.Forum;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class ForumsBBSManager extends BaseBBSManager
{
private static Logger LOGGER = Logger.getLogger(ForumsBBSManager.class.getName());
private final List<Forum> _table;
private int _lastid = 1;
/**
* Instantiates a new forums bbs manager.
*/
protected ForumsBBSManager()
{
_table = new CopyOnWriteArrayList<>();
try (Connection con = DatabaseFactory.getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT forum_id FROM forums WHERE forum_type = 0"))
{
while (rs.next())
{
addForum(new Forum(rs.getInt("forum_id"), null));
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Data error on Forum (root): " + e.getMessage(), e);
}
}
/**
* Inits the root.
*/
public void initRoot()
{
_table.forEach(f -> f.vload());
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _table.size() + " forums. Last forum id used: " + _lastid);
}
/**
* Adds the forum.
* @param ff the forum
*/
public void addForum(Forum ff)
{
if (ff == null)
{
return;
}
_table.add(ff);
if (ff.getID() > _lastid)
{
_lastid = ff.getID();
}
}
@Override
public void parsecmd(String command, L2PcInstance activeChar)
{
}
/**
* Gets the forum by name.
* @param name the forum name
* @return the forum by name
*/
public Forum getForumByName(String name)
{
for (Forum f : _table)
{
if (f.getName().equals(name))
{
return f;
}
}
return null;
}
/**
* Creates the new forum.
* @param name the forum name
* @param parent the parent forum
* @param type the forum type
* @param perm the perm
* @param oid the oid
* @return the new forum
*/
public Forum createNewForum(String name, Forum parent, int type, int perm, int oid)
{
final Forum forum = new Forum(name, parent, type, perm, oid);
forum.insertIntoDb();
return forum;
}
/**
* Gets the a new Id.
* @return the a new Id
*/
public int getANewID()
{
return ++_lastid;
}
/**
* Gets the forum by Id.
* @param idf the the forum Id
* @return the forum by Id
*/
public Forum getForumByID(int idf)
{
for (Forum f : _table)
{
if (f.getID() == idf)
{
return f;
}
}
return null;
}
@Override
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
}
/**
* Gets the single instance of ForumsBBSManager.
* @return single instance of ForumsBBSManager
*/
public static ForumsBBSManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ForumsBBSManager _instance = new ForumsBBSManager();
}
}

View File

@@ -0,0 +1,188 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import com.l2jmobius.gameserver.communitybbs.BB.Forum;
import com.l2jmobius.gameserver.communitybbs.BB.Post;
import com.l2jmobius.gameserver.communitybbs.BB.Topic;
import com.l2jmobius.gameserver.handler.CommunityBoardHandler;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class PostBBSManager extends BaseBBSManager
{
private final Map<Topic, Post> _postByTopic = new ConcurrentHashMap<>();
public Post getGPosttByTopic(Topic t)
{
Post post = _postByTopic.get(t);
if (post == null)
{
post = new Post(t);
_postByTopic.put(t, post);
}
return post;
}
public void delPostByTopic(Topic t)
{
_postByTopic.remove(t);
}
public void addPostByTopic(Post p, Topic t)
{
if (_postByTopic.get(t) == null)
{
_postByTopic.put(t, p);
}
}
@Override
public void parsecmd(String command, L2PcInstance activeChar)
{
if (command.startsWith("_bbsposts;read;"))
{
final StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
final int idf = Integer.parseInt(st.nextToken());
final int idp = Integer.parseInt(st.nextToken());
final String index = st.hasMoreTokens() ? st.nextToken() : null;
final int ind = index == null ? 1 : Integer.parseInt(index);
showPost(TopicBBSManager.getInstance().getTopicByID(idp), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind);
}
else if (command.startsWith("_bbsposts;edit;"))
{
final StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
final int idf = Integer.parseInt(st.nextToken());
final int idt = Integer.parseInt(st.nextToken());
final int idp = Integer.parseInt(st.nextToken());
showEditPost(TopicBBSManager.getInstance().getTopicByID(idt), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idp);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the command: " + command + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showEditPost(Topic topic, Forum forum, L2PcInstance activeChar, int idp)
{
final Post p = getGPosttByTopic(topic);
if ((forum == null) || (topic == null) || (p == null))
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>Error, this forum, topic or post does not exist!</center><br><br></body></html>", activeChar);
}
else
{
showHtmlEditPost(topic, activeChar, forum, p);
}
}
private void showPost(Topic topic, Forum forum, L2PcInstance activeChar, int ind)
{
if ((forum == null) || (topic == null))
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>Error: This forum is not implemented yet!</center></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoPost(topic, activeChar, forum);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>The forum: " + forum.getName() + " is not implemented yet!</center></body></html>", activeChar);
}
}
private void showHtmlEditPost(Topic topic, L2PcInstance activeChar, Forum forum, Post p)
{
final String html = "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">" + forum.getName() + " Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0><tr><td width=610><img src=\"sek.cbui355\" width=\"610\" height=\"1\"><br1><img src=\"sek.cbui355\" width=\"610\" height=\"1\"></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=20></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&$413;</td><td FIXWIDTH=540>" + topic.getName() + "</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29 valign=top>&$427;</td><td align=center FIXWIDTH=540><MultiEdit var =\"Content\" width=535 height=313></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&nbsp;</td><td align=center FIXWIDTH=70><button value=\"&$140;\" action=\"Write Post " + forum.getID() + ";" + topic.getID() + ";0 _ Content Content Content\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td><td align=center FIXWIDTH=70><button value = \"&$141;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td><td align=center FIXWIDTH=400>&nbsp;</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>";
send1001(html, activeChar);
send1002(activeChar, p.getCPost(0).postTxt, topic.getName(), DateFormat.getInstance().format(new Date(topic.getDate())));
}
private void showMemoPost(Topic topic, L2PcInstance activeChar, Forum forum)
{
final Post p = getGPosttByTopic(topic);
final Locale locale = Locale.getDefault();
final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
String mes = p.getCPost(0).postTxt.replace(">", "&gt;");
mes = mes.replace("<", "&lt;");
final String html = "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0 bgcolor=333333><tr><td height=10></td></tr><tr><td fixWIDTH=55 align=right valign=top>&$413; : &nbsp;</td><td fixWIDTH=380 valign=top>" + topic.getName() + "</td><td fixwidth=5></td><td fixwidth=50></td><td fixWIDTH=120></td></tr><tr><td height=10></td></tr><tr><td align=right><font color=\"AAAAAA\" >&$417; : &nbsp;</font></td><td><font color=\"AAAAAA\">" + topic.getOwnerName() + "</font></td><td></td><td><font color=\"AAAAAA\">&$418; :</font></td><td><font color=\"AAAAAA\">" + dateFormat.format(p.getCPost(0).postDate) + "</font></td></tr><tr><td height=10></td></tr></table><br><table border=0 cellspacing=0 cellpadding=0><tr><td fixwidth=5></td><td FIXWIDTH=600 align=left>" + mes + "</td><td fixqqwidth=5></td></tr></table><br><img src=\"L2UI.squareblank\" width=\"1\" height=\"5\"><img src=\"L2UI.squaregray\" width=\"610\" height=\"1\"><img src=\"L2UI.squareblank\" width=\"1\" height=\"5\"><table border=0 cellspacing=0 cellpadding=0 FIXWIDTH=610><tr><td width=50><button value=\"&$422;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td><td width=560 align=right><table border=0 cellspacing=0><tr><td FIXWIDTH=300></td><td><button value = \"&$424;\" action=\"bypass _bbsposts;edit;" + forum.getID() + ";" + topic.getID() + ";0\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;<td><button value = \"&$425;\" action=\"bypass _bbstopics;del;" + forum.getID() + ";" + topic.getID() + "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;<td><button value = \"&$421;\" action=\"bypass _bbstopics;crea;" + forum.getID() + "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;</tr></table></td></tr></table><br><br><br></center></body></html>";
CommunityBoardHandler.separateAndSend(html, activeChar);
}
@Override
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
final StringTokenizer st = new StringTokenizer(ar1, ";");
final int idf = Integer.parseInt(st.nextToken());
final int idt = Integer.parseInt(st.nextToken());
final int idp = Integer.parseInt(st.nextToken());
final Forum f = ForumsBBSManager.getInstance().getForumByID(idf);
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
final Topic t = f.getTopic(idt);
if (t == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + idt + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
final Post p = getGPosttByTopic(t);
if (p != null)
{
if (p.getCPost(idp) == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the post: " + idp + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
p.getCPost(idp).postTxt = ar4;
p.updatetxt(idp);
parsecmd("_bbsposts;read;" + f.getID() + ";" + t.getID(), activeChar);
}
}
}
}
}
public static PostBBSManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PostBBSManager _instance = new PostBBSManager();
}
}

View File

@@ -0,0 +1,306 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import com.l2jmobius.gameserver.communitybbs.BB.Forum;
import com.l2jmobius.gameserver.communitybbs.BB.Post;
import com.l2jmobius.gameserver.communitybbs.BB.Topic;
import com.l2jmobius.gameserver.data.sql.impl.ClanTable;
import com.l2jmobius.gameserver.handler.CommunityBoardHandler;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class TopicBBSManager extends BaseBBSManager
{
private final List<Topic> _table = new CopyOnWriteArrayList<>();
private final Map<Forum, Integer> _maxId = new ConcurrentHashMap<>();
protected TopicBBSManager()
{
// Prevent external initialization.
}
public void addTopic(Topic tt)
{
_table.add(tt);
}
public void delTopic(Topic topic)
{
_table.remove(topic);
}
public void setMaxID(int id, Forum f)
{
_maxId.put(f, id);
}
public int getMaxID(Forum f)
{
final Integer i = _maxId.get(f);
return i == null ? 0 : i;
}
public Topic getTopicByID(int idf)
{
for (Topic t : _table)
{
if (t.getID() == idf)
{
return t;
}
}
return null;
}
@Override
public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
if (ar1.equals("crea"))
{
final Forum f = ForumsBBSManager.getInstance().getForumByID(Integer.parseInt(ar2));
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + ar2 + " is not implemented yet</center><br><br></body></html>", activeChar);
}
else
{
f.vload();
final Topic t = new Topic(Topic.ConstructorType.CREATE, getInstance().getMaxID(f) + 1, Integer.parseInt(ar2), ar5, Calendar.getInstance().getTimeInMillis(), activeChar.getName(), activeChar.getObjectId(), Topic.MEMO, 0);
f.addTopic(t);
getInstance().setMaxID(t.getID(), f);
final Post p = new Post(activeChar.getName(), activeChar.getObjectId(), Calendar.getInstance().getTimeInMillis(), t.getID(), f.getID(), ar4);
PostBBSManager.getInstance().addPostByTopic(p, t);
parsecmd("_bbsmemo", activeChar);
}
}
else if (ar1.equals("del"))
{
final Forum f = ForumsBBSManager.getInstance().getForumByID(Integer.parseInt(ar2));
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + ar2 + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
final Topic t = f.getTopic(Integer.parseInt(ar3));
if (t == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + ar3 + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
// CPost cp = null;
final Post p = PostBBSManager.getInstance().getGPosttByTopic(t);
if (p != null)
{
p.deleteme(t);
}
t.deleteme(f);
parsecmd("_bbsmemo", activeChar);
}
}
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the command: " + ar1 + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
@Override
public void parsecmd(String command, L2PcInstance activeChar)
{
if (command.equals("_bbsmemo"))
{
showTopics(activeChar.getMemo(), activeChar, 1, activeChar.getMemo().getID());
}
else if (command.startsWith("_bbstopics;read"))
{
final StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
final int idf = Integer.parseInt(st.nextToken());
final String index = st.hasMoreTokens() ? st.nextToken() : null;
final int ind = index == null ? 1 : Integer.parseInt(index);
showTopics(ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind, idf);
}
else if (command.startsWith("_bbstopics;crea"))
{
final StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
final int idf = Integer.parseInt(st.nextToken());
showNewTopic(ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idf);
}
else if (command.startsWith("_bbstopics;del"))
{
final StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
final int idf = Integer.parseInt(st.nextToken());
final int idt = Integer.parseInt(st.nextToken());
final Forum f = ForumsBBSManager.getInstance().getForumByID(idf);
if (f == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
final Topic t = f.getTopic(idt);
if (t == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the topic: " + idt + " does not exist !</center><br><br></body></html>", activeChar);
}
else
{
// CPost cp = null;
final Post p = PostBBSManager.getInstance().getGPosttByTopic(t);
if (p != null)
{
p.deleteme(t);
}
t.deleteme(f);
parsecmd("_bbsmemo", activeChar);
}
}
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the command: " + command + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showNewTopic(Forum forum, L2PcInstance activeChar, int idf)
{
if (forum == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " is not implemented yet</center><br><br></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoNewTopics(forum, activeChar);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + forum.getName() + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showMemoNewTopics(Forum forum, L2PcInstance activeChar)
{
final String html = "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0><tr><td width=610><img src=\"sek.cbui355\" width=\"610\" height=\"1\"><br1><img src=\"sek.cbui355\" width=\"610\" height=\"1\"></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=20></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&$413;</td><td FIXWIDTH=540><edit var = \"Title\" width=540 height=13></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29 valign=top>&$427;</td><td align=center FIXWIDTH=540><MultiEdit var =\"Content\" width=535 height=313></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&nbsp;</td><td align=center FIXWIDTH=70><button value=\"&$140;\" action=\"Write Topic crea " + forum.getID() + " Title Content Title\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td><td align=center FIXWIDTH=70><button value = \"&$141;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td><td align=center FIXWIDTH=400>&nbsp;</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>";
send1001(html, activeChar);
send1002(activeChar);
}
private void showTopics(Forum forum, L2PcInstance activeChar, int index, int idf)
{
if (forum == null)
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + idf + " is not implemented yet</center><br><br></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoTopics(forum, activeChar, index);
}
else
{
CommunityBoardHandler.separateAndSend("<html><body><br><br><center>the forum: " + forum.getName() + " is not implemented yet</center><br><br></body></html>", activeChar);
}
}
private void showMemoTopics(Forum forum, L2PcInstance activeChar, int index)
{
forum.vload();
final StringBuilder html = new StringBuilder(2000);
html.append("<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=2 bgcolor=888888 width=610><tr><td FIXWIDTH=5></td><td FIXWIDTH=415 align=center>&$413;</td><td FIXWIDTH=120 align=center></td><td FIXWIDTH=70 align=center>&$418;</td></tr></table>");
final DateFormat dateFormat = DateFormat.getInstance();
for (int i = 0, j = getMaxID(forum) + 1; i < (12 * index); j--)
{
if (j < 0)
{
break;
}
final Topic t = forum.getTopic(j);
if (t != null)
{
if (i++ >= (12 * (index - 1)))
{
html.append("<table border=0 cellspacing=0 cellpadding=5 WIDTH=610><tr><td FIXWIDTH=5></td><td FIXWIDTH=415><a action=\"bypass _bbsposts;read;" + forum.getID() + ";" + t.getID() + "\">" + t.getName() + "</a></td><td FIXWIDTH=120 align=center></td><td FIXWIDTH=70 align=center>" + dateFormat.format(new Date(t.getDate())) + "</td></tr></table><img src=\"L2UI.Squaregray\" width=\"610\" height=\"1\">");
}
}
}
html.append("<br><table width=610 cellspace=0 cellpadding=0><tr><td width=50><button value=\"&$422;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td><td width=510 align=center><table border=0><tr>");
if (index == 1)
{
html.append("<td><button action=\"\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
else
{
html.append("<td><button action=\"bypass _bbstopics;read;" + forum.getID() + ";" + (index - 1) + "\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
int nbp = forum.getTopicSize() / 8;
if ((nbp * 8) != ClanTable.getInstance().getClanCount())
{
nbp++;
}
for (int i = 1; i <= nbp; i++)
{
if (i == index)
{
html.append("<td> " + i + " </td>");
}
else
{
html.append("<td><a action=\"bypass _bbstopics;read;" + forum.getID() + ";" + i + "\"> " + i + " </a></td>");
}
}
if (index == nbp)
{
html.append("<td><button action=\"\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
else
{
html.append("<td><button action=\"bypass _bbstopics;read;" + forum.getID() + ";" + (index + 1) + "\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
html.append("</tr></table> </td> <td align=right><button value = \"&$421;\" action=\"bypass _bbstopics;crea;" + forum.getID() + "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr> <td></td><td align=center><table border=0><tr><td></td><td><edit var = \"Search\" width=130 height=11></td><td><button value=\"&$420;\" action=\"Write 5 -2 0 Search _ _\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td> </tr></table> </td></tr></table><br><br><br></center></body></html>");
CommunityBoardHandler.separateAndSend(html.toString(), activeChar);
}
public static TopicBBSManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final TopicBBSManager _instance = new TopicBBSManager();
}
}

View File

@@ -0,0 +1,172 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.enums.ChatType;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.announce.Announcement;
import com.l2jmobius.gameserver.model.announce.AnnouncementType;
import com.l2jmobius.gameserver.model.announce.AutoAnnouncement;
import com.l2jmobius.gameserver.model.announce.IAnnouncement;
import com.l2jmobius.gameserver.network.serverpackets.CreatureSay;
/**
* Loads announcements from database.
* @author UnAfraid
*/
public final class AnnouncementsTable
{
private static Logger LOGGER = Logger.getLogger(AnnouncementsTable.class.getName());
private final Map<Integer, IAnnouncement> _announcements = new ConcurrentSkipListMap<>();
protected AnnouncementsTable()
{
load();
}
private void load()
{
_announcements.clear();
try (Connection con = DatabaseFactory.getConnection();
Statement st = con.createStatement();
ResultSet rset = st.executeQuery("SELECT * FROM announcements"))
{
while (rset.next())
{
final AnnouncementType type = AnnouncementType.findById(rset.getInt("type"));
final Announcement announce;
switch (type)
{
case NORMAL:
case CRITICAL:
{
announce = new Announcement(rset);
break;
}
case AUTO_NORMAL:
case AUTO_CRITICAL:
{
announce = new AutoAnnouncement(rset);
break;
}
default:
{
continue;
}
}
_announcements.put(announce.getId(), announce);
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Failed loading announcements:", e);
}
}
/**
* Sending all announcements to the player
* @param player
*/
public void showAnnouncements(L2PcInstance player)
{
sendAnnouncements(player, AnnouncementType.NORMAL);
sendAnnouncements(player, AnnouncementType.CRITICAL);
sendAnnouncements(player, AnnouncementType.EVENT);
}
/**
* Sends all announcements to the player by the specified type
* @param player
* @param type
*/
private void sendAnnouncements(L2PcInstance player, AnnouncementType type)
{
for (IAnnouncement announce : _announcements.values())
{
if (announce.isValid() && (announce.getType() == type))
{
player.sendPacket(new CreatureSay(0, //
type == AnnouncementType.CRITICAL ? ChatType.CRITICAL_ANNOUNCE : ChatType.ANNOUNCEMENT, //
player.getName(), announce.getContent()));
}
}
}
/**
* Adds announcement
* @param announce
*/
public void addAnnouncement(IAnnouncement announce)
{
if (announce.storeMe())
{
_announcements.put(announce.getId(), announce);
}
}
/**
* Removes announcement by id
* @param id
* @return {@code true} if announcement exists and was deleted successfully, {@code false} otherwise.
*/
public boolean deleteAnnouncement(int id)
{
final IAnnouncement announce = _announcements.remove(id);
return (announce != null) && announce.deleteMe();
}
/**
* @param id
* @return {@link IAnnouncement} by id
*/
public IAnnouncement getAnnounce(int id)
{
return _announcements.get(id);
}
/**
* @return {@link Collection} containing all announcements
*/
public Collection<IAnnouncement> getAllAnnouncements()
{
return _announcements.values();
}
/**
* @return Single instance of {@link AnnouncementsTable}
*/
public static AnnouncementsTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AnnouncementsTable _instance = new AnnouncementsTable();
}
}

View File

@@ -0,0 +1,311 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* Loads name and access level for all players.
* @version 2005/03/27
*/
public class CharNameTable
{
private static Logger LOGGER = Logger.getLogger(CharNameTable.class.getName());
private final Map<Integer, String> _chars = new ConcurrentHashMap<>();
private final Map<Integer, Integer> _accessLevels = new ConcurrentHashMap<>();
protected CharNameTable()
{
if (Config.CACHE_CHAR_NAMES)
{
loadAll();
}
}
public final void addName(L2PcInstance player)
{
if (player != null)
{
addName(player.getObjectId(), player.getName());
_accessLevels.put(player.getObjectId(), player.getAccessLevel().getLevel());
}
}
private final void addName(int objectId, String name)
{
if (name != null)
{
if (!name.equals(_chars.get(objectId)))
{
_chars.put(objectId, name);
}
}
}
public final void removeName(int objId)
{
_chars.remove(objId);
_accessLevels.remove(objId);
}
public final int getIdByName(String name)
{
if ((name == null) || name.isEmpty())
{
return -1;
}
for (Entry<Integer, String> entry : _chars.entrySet())
{
if (entry.getValue().equalsIgnoreCase(name))
{
return entry.getKey();
}
}
if (Config.CACHE_CHAR_NAMES)
{
return -1;
}
int id = -1;
int accessLevel = 0;
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT charId,accesslevel FROM characters WHERE char_name=?"))
{
ps.setString(1, name);
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
id = rs.getInt("charId");
accessLevel = rs.getInt("accesslevel");
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char name: " + e.getMessage(), e);
}
if (id > 0)
{
_chars.put(id, name);
_accessLevels.put(id, accessLevel);
return id;
}
return -1; // not found
}
public final String getNameById(int id)
{
if (id <= 0)
{
return null;
}
String name = _chars.get(id);
if (name != null)
{
return name;
}
if (Config.CACHE_CHAR_NAMES)
{
return null;
}
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT char_name,accesslevel FROM characters WHERE charId=?"))
{
ps.setInt(1, id);
try (ResultSet rset = ps.executeQuery())
{
if (rset.next())
{
name = rset.getString("char_name");
_chars.put(id, name);
_accessLevels.put(id, rset.getInt("accesslevel"));
return name;
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char id: " + e.getMessage(), e);
}
return null; // not found
}
public final int getAccessLevelById(int objectId)
{
return getNameById(objectId) != null ? _accessLevels.get(objectId) : 0;
}
public synchronized boolean doesCharNameExist(String name)
{
boolean result = false;
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) as count FROM characters WHERE char_name=?"))
{
ps.setString(1, name);
try (ResultSet rs = ps.executeQuery())
{
if (rs.next())
{
result = rs.getInt("count") > 0;
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing charname: " + e.getMessage(), e);
}
return result;
}
public int getAccountCharacterCount(String account)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT COUNT(char_name) as count FROM characters WHERE account_name=?"))
{
ps.setString(1, account);
try (ResultSet rset = ps.executeQuery())
{
if (rset.next())
{
return rset.getInt("count");
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "Couldn't retrieve account for id: " + e.getMessage(), e);
}
return 0;
}
public int getLevelById(int objectId)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT level FROM characters WHERE charId = ?"))
{
ps.setInt(1, objectId);
try (ResultSet rset = ps.executeQuery())
{
if (rset.next())
{
return rset.getInt("level");
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char count: " + e.getMessage(), e);
}
return 0;
}
public int getClassIdById(int objectId)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT classid FROM characters WHERE charId = ?"))
{
ps.setInt(1, objectId);
try (ResultSet rset = ps.executeQuery())
{
if (rset.next())
{
return rset.getInt("classid");
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't retrieve class for id: " + e.getMessage(), e);
}
return 0;
}
public int getClanIdById(int objectId)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT clanId FROM characters WHERE charId = ?"))
{
ps.setInt(1, objectId);
try (ResultSet rset = ps.executeQuery())
{
while (rset.next())
{
return rset.getInt("clanId");
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char count: " + e.getMessage(), e);
}
return 0;
}
private void loadAll()
{
try (Connection con = DatabaseFactory.getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT charId, char_name, accesslevel FROM characters"))
{
while (rs.next())
{
final int id = rs.getInt("charId");
_chars.put(id, rs.getString("char_name"));
_accessLevels.put(id, rs.getInt("accesslevel"));
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't retrieve all char id/name/access: " + e.getMessage(), e);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _chars.size() + " char names.");
}
public static CharNameTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CharNameTable _instance = new CharNameTable();
}
}

View File

@@ -0,0 +1,259 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
import com.l2jmobius.gameserver.data.xml.impl.PetDataTable;
import com.l2jmobius.gameserver.data.xml.impl.SkillData;
import com.l2jmobius.gameserver.model.L2PetData;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PetInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2ServitorInstance;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.serverpackets.PetItemList;
/**
* @author Nyaran
*/
public class CharSummonTable
{
private static final Logger LOGGER = Logger.getLogger(CharSummonTable.class.getName());
private static final Map<Integer, Integer> _pets = new ConcurrentHashMap<>();
private static final Map<Integer, Set<Integer>> _servitors = new ConcurrentHashMap<>();
// SQL
private static final String INIT_PET = "SELECT ownerId, item_obj_id FROM pets WHERE restore = 'true'";
private static final String INIT_SUMMONS = "SELECT ownerId, summonId FROM character_summons";
private static final String LOAD_SUMMON = "SELECT summonSkillId, summonId, curHp, curMp, time FROM character_summons WHERE ownerId = ?";
private static final String REMOVE_SUMMON = "DELETE FROM character_summons WHERE ownerId = ? and summonId = ?";
private static final String SAVE_SUMMON = "REPLACE INTO character_summons (ownerId,summonId,summonSkillId,curHp,curMp,time) VALUES (?,?,?,?,?,?)";
public Map<Integer, Integer> getPets()
{
return _pets;
}
public Map<Integer, Set<Integer>> getServitors()
{
return _servitors;
}
public void init()
{
if (Config.RESTORE_SERVITOR_ON_RECONNECT)
{
try (Connection con = DatabaseFactory.getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(INIT_SUMMONS))
{
while (rs.next())
{
_servitors.computeIfAbsent(rs.getInt("ownerId"), k -> ConcurrentHashMap.newKeySet()).add(rs.getInt("summonId"));
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Error while loading saved servitor: " + e);
}
}
if (Config.RESTORE_PET_ON_RECONNECT)
{
try (Connection con = DatabaseFactory.getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(INIT_PET))
{
while (rs.next())
{
_pets.put(rs.getInt("ownerId"), rs.getInt("item_obj_id"));
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Error while loading saved pet: " + e);
}
}
}
public void removeServitor(L2PcInstance activeChar, int summonObjectId)
{
_servitors.computeIfPresent(activeChar.getObjectId(), (k, v) ->
{
v.remove(summonObjectId);
return !v.isEmpty() ? v : null;
});
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement(REMOVE_SUMMON))
{
ps.setInt(1, activeChar.getObjectId());
ps.setInt(2, summonObjectId);
ps.execute();
}
catch (SQLException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Summon cannot be removed: " + e);
}
}
public void restorePet(L2PcInstance activeChar)
{
final L2ItemInstance item = activeChar.getInventory().getItemByObjectId(_pets.get(activeChar.getObjectId()));
if (item == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet summoning item for: " + activeChar);
return;
}
final L2PetData petData = PetDataTable.getInstance().getPetDataByItemId(item.getId());
if (petData == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet data for: " + activeChar + " and summoning item: " + item);
return;
}
final L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(petData.getNpcId());
if (npcTemplate == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet NPC template for: " + activeChar + " and pet Id:" + petData.getNpcId());
return;
}
final L2PetInstance pet = L2PetInstance.spawnPet(npcTemplate, activeChar, item);
if (pet == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Null pet instance for: " + activeChar + " and pet NPC template:" + npcTemplate);
return;
}
pet.setShowSummonAnimation(true);
pet.setTitle(activeChar.getName());
if (!pet.isRespawned())
{
pet.setCurrentHp(pet.getMaxHp());
pet.setCurrentMp(pet.getMaxMp());
pet.getStat().setExp(pet.getExpForThisLevel());
pet.setCurrentFed(pet.getMaxFed());
}
pet.setRunning();
if (!pet.isRespawned())
{
pet.storeMe();
}
item.setEnchantLevel(pet.getLevel());
activeChar.setPet(pet);
pet.spawnMe(activeChar.getX() + 50, activeChar.getY() + 100, activeChar.getZ());
pet.startFeed();
pet.setFollowStatus(true);
pet.getOwner().sendPacket(new PetItemList(pet.getInventory().getItems()));
pet.broadcastStatusUpdate();
}
public void restoreServitor(L2PcInstance activeChar)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement(LOAD_SUMMON))
{
ps.setInt(1, activeChar.getObjectId());
try (ResultSet rs = ps.executeQuery())
{
Skill skill;
while (rs.next())
{
final int summonObjId = rs.getInt("summonId");
final int skillId = rs.getInt("summonSkillId");
final int curHp = rs.getInt("curHp");
final int curMp = rs.getInt("curMp");
final int time = rs.getInt("time");
skill = SkillData.getInstance().getSkill(skillId, activeChar.getSkillLevel(skillId));
if (skill == null)
{
removeServitor(activeChar, summonObjId);
return;
}
skill.applyEffects(activeChar, activeChar);
if (activeChar.hasServitors())
{
final L2ServitorInstance summon = activeChar.getServitors().values().stream().map(s -> ((L2ServitorInstance) s)).filter(s -> s.getReferenceSkill() == skillId).findAny().orElse(null);
summon.setCurrentHp(curHp);
summon.setCurrentMp(curMp);
summon.setLifeTimeRemaining(time);
}
}
}
}
catch (SQLException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Servitor cannot be restored: " + e);
}
}
public void saveSummon(L2ServitorInstance summon)
{
if ((summon == null) || (summon.getLifeTimeRemaining() <= 0))
{
return;
}
_servitors.computeIfAbsent(summon.getOwner().getObjectId(), k -> ConcurrentHashMap.newKeySet()).add(summon.getObjectId());
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement(SAVE_SUMMON))
{
ps.setInt(1, summon.getOwner().getObjectId());
ps.setInt(2, summon.getObjectId());
ps.setInt(3, summon.getReferenceSkill());
ps.setInt(4, (int) Math.round(summon.getCurrentHp()));
ps.setInt(5, (int) Math.round(summon.getCurrentMp()));
ps.setInt(6, summon.getLifeTimeRemaining());
ps.execute();
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Failed to store summon: " + summon + " from " + summon.getOwner() + ", error: " + e);
}
}
public static CharSummonTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CharSummonTable _instance = new CharSummonTable();
}
}

View File

@@ -0,0 +1,485 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPool;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jmobius.gameserver.data.xml.impl.ClanHallData;
import com.l2jmobius.gameserver.enums.UserInfoType;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.instancemanager.ClanEntryManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
import com.l2jmobius.gameserver.instancemanager.FortSiegeManager;
import com.l2jmobius.gameserver.instancemanager.SiegeManager;
import com.l2jmobius.gameserver.model.ClanPrivilege;
import com.l2jmobius.gameserver.model.ClanWar;
import com.l2jmobius.gameserver.model.ClanWar.ClanWarState;
import com.l2jmobius.gameserver.model.L2Clan;
import com.l2jmobius.gameserver.model.L2ClanMember;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.entity.ClanHall;
import com.l2jmobius.gameserver.model.entity.Fort;
import com.l2jmobius.gameserver.model.entity.FortSiege;
import com.l2jmobius.gameserver.model.entity.Siege;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerClanCreate;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerClanDestroy;
import com.l2jmobius.gameserver.model.events.impl.clan.OnClanWarFinish;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.PledgeShowInfoUpdate;
import com.l2jmobius.gameserver.network.serverpackets.PledgeShowMemberListAll;
import com.l2jmobius.gameserver.network.serverpackets.PledgeShowMemberListUpdate;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import com.l2jmobius.gameserver.util.EnumIntBitmask;
import com.l2jmobius.gameserver.util.Util;
/**
* This class loads the clan related data.
*/
public class ClanTable
{
private static final Logger LOGGER = Logger.getLogger(ClanTable.class.getName());
private final Map<Integer, L2Clan> _clans = new ConcurrentHashMap<>();
protected ClanTable()
{
// forums has to be loaded before clan data, because of last forum id used should have also memo included
if (Config.ENABLE_COMMUNITY_BOARD)
{
ForumsBBSManager.getInstance().initRoot();
}
// Get all clan ids.
final List<Integer> cids = new ArrayList<>();
try (Connection con = DatabaseFactory.getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT clan_id FROM clan_data"))
{
while (rs.next())
{
cids.add(rs.getInt("clan_id"));
}
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "Error restoring ClanTable.", e);
}
// Initialize clans.
for (int cid : cids)
{
final L2Clan clan = new L2Clan(cid);
_clans.put(cid, clan);
if (clan.getDissolvingExpiryTime() != 0)
{
scheduleRemoveClan(clan.getId());
}
}
LOGGER.info(getClass().getSimpleName() + ": Restored " + cids.size() + " clans from the database.");
allianceCheck();
restoreClanWars();
}
/**
* Gets the clans.
* @return the clans
*/
public Collection<L2Clan> getClans()
{
return _clans.values();
}
/**
* Gets the clan count.
* @return the clan count
*/
public int getClanCount()
{
return _clans.size();
}
/**
* @param clanId
* @return
*/
public L2Clan getClan(int clanId)
{
return _clans.get(clanId);
}
public L2Clan getClanByName(String clanName)
{
return _clans.values().stream().filter(c -> c.getName().equalsIgnoreCase(clanName)).findFirst().orElse(null);
}
/**
* Creates a new clan and store clan info to database
* @param player
* @param clanName
* @return NULL if clan with same name already exists
*/
public L2Clan createClan(L2PcInstance player, String clanName)
{
if (player == null)
{
return null;
}
if (player.getLevel() < 10)
{
player.sendPacket(SystemMessageId.YOU_DO_NOT_MEET_THE_CRITERIA_IN_ORDER_TO_CREATE_A_CLAN);
return null;
}
if (player.getClanId() != 0)
{
player.sendPacket(SystemMessageId.YOU_HAVE_FAILED_TO_CREATE_A_CLAN);
return null;
}
if (System.currentTimeMillis() < player.getClanCreateExpiryTime())
{
player.sendPacket(SystemMessageId.YOU_MUST_WAIT_10_DAYS_BEFORE_CREATING_A_NEW_CLAN);
return null;
}
if (!Util.isAlphaNumeric(clanName) || (clanName.length() < 2))
{
player.sendPacket(SystemMessageId.CLAN_NAME_IS_INVALID);
return null;
}
if (clanName.length() > 16)
{
player.sendPacket(SystemMessageId.CLAN_NAME_S_LENGTH_IS_INCORRECT);
return null;
}
if (getClanByName(clanName) != null)
{
// clan name is already taken
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_ALREADY_EXISTS);
sm.addString(clanName);
player.sendPacket(sm);
return null;
}
final L2Clan clan = new L2Clan(IdFactory.getInstance().getNextId(), clanName);
final L2ClanMember leader = new L2ClanMember(clan, player);
clan.setLeader(leader);
leader.setPlayerInstance(player);
clan.store();
player.setClan(clan);
player.setPledgeClass(L2ClanMember.calculatePledgeClass(player));
player.setClanPrivileges(new EnumIntBitmask<>(ClanPrivilege.class, true));
_clans.put(Integer.valueOf(clan.getId()), clan);
// should be update packet only
player.sendPacket(new PledgeShowInfoUpdate(clan));
PledgeShowMemberListAll.sendAllTo(player);
player.sendPacket(new PledgeShowMemberListUpdate(player));
player.sendPacket(SystemMessageId.YOUR_CLAN_HAS_BEEN_CREATED);
player.broadcastUserInfo(UserInfoType.RELATION, UserInfoType.CLAN);
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerClanCreate(player, clan));
return clan;
}
public synchronized void destroyClan(int clanId)
{
final L2Clan clan = getClan(clanId);
if (clan == null)
{
return;
}
clan.broadcastToOnlineMembers(SystemMessage.getSystemMessage(SystemMessageId.CLAN_HAS_DISPERSED));
ClanEntryManager.getInstance().removeFromClanList(clan.getId());
final int castleId = clan.getCastleId();
if (castleId == 0)
{
for (Siege siege : SiegeManager.getInstance().getSieges())
{
siege.removeSiegeClan(clan);
}
}
final int fortId = clan.getFortId();
if (fortId == 0)
{
for (FortSiege siege : FortSiegeManager.getInstance().getSieges())
{
siege.removeAttacker(clan);
}
}
final ClanHall hall = ClanHallData.getInstance().getClanHallByClan(clan);
if (hall != null)
{
hall.setOwner(null);
}
final L2ClanMember leaderMember = clan.getLeader();
if (leaderMember == null)
{
clan.getWarehouse().destroyAllItems("ClanRemove", null, null);
}
else
{
clan.getWarehouse().destroyAllItems("ClanRemove", clan.getLeader().getPlayerInstance(), null);
}
for (L2ClanMember member : clan.getMembers())
{
clan.removeClanMember(member.getObjectId(), 0);
}
_clans.remove(clanId);
IdFactory.getInstance().releaseId(clanId);
try (Connection con = DatabaseFactory.getConnection())
{
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_data WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_privs WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_skills WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_subpledges WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_wars WHERE clan1=? OR clan2=?"))
{
ps.setInt(1, clanId);
ps.setInt(2, clanId);
ps.execute();
}
try (PreparedStatement ps = con.prepareStatement("DELETE FROM clan_notices WHERE clan_id=?"))
{
ps.setInt(1, clanId);
ps.execute();
}
if (fortId != 0)
{
final Fort fort = FortManager.getInstance().getFortById(fortId);
if (fort != null)
{
final L2Clan owner = fort.getOwnerClan();
if (clan == owner)
{
fort.removeOwner(true);
}
}
}
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error removing clan from DB.", e);
}
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerClanDestroy(leaderMember, clan));
}
public void scheduleRemoveClan(int clanId)
{
ThreadPool.schedule(() ->
{
if (getClan(clanId) == null)
{
return;
}
if (getClan(clanId).getDissolvingExpiryTime() != 0)
{
destroyClan(clanId);
}
}, Math.max(getClan(clanId).getDissolvingExpiryTime() - System.currentTimeMillis(), 300000));
}
public boolean isAllyExists(String allyName)
{
for (L2Clan clan : _clans.values())
{
if ((clan.getAllyName() != null) && clan.getAllyName().equalsIgnoreCase(allyName))
{
return true;
}
}
return false;
}
public void storeClanWars(ClanWar war)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("REPLACE INTO clan_wars (clan1, clan2, clan1Kill, clan2Kill, winnerClan, startTime, endTime, state) VALUES(?,?,?,?,?,?,?,?)"))
{
ps.setInt(1, war.getAttackerClanId());
ps.setInt(2, war.getAttackedClanId());
ps.setInt(3, war.getAttackerKillCount());
ps.setInt(4, war.getAttackedKillCount());
ps.setInt(5, war.getWinnerClanId());
ps.setLong(6, war.getStartTime());
ps.setLong(7, war.getEndTime());
ps.setInt(8, war.getState().ordinal());
ps.execute();
}
catch (Exception e)
{
LOGGER.severe("Error storing clan wars data: " + e);
}
}
public void deleteClanWars(int clanId1, int clanId2)
{
final L2Clan clan1 = getInstance().getClan(clanId1);
final L2Clan clan2 = getInstance().getClan(clanId2);
EventDispatcher.getInstance().notifyEventAsync(new OnClanWarFinish(clan1, clan2));
clan1.deleteWar(clan2.getId());
clan2.deleteWar(clan1.getId());
clan1.broadcastClanStatus();
clan2.broadcastClanStatus();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM clan_wars WHERE clan1=? AND clan2=?"))
{
ps.setInt(1, clanId1);
ps.setInt(2, clanId2);
ps.execute();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error removing clan wars data.", e);
}
}
private void restoreClanWars()
{
try (Connection con = DatabaseFactory.getConnection();
Statement statement = con.createStatement();
ResultSet rset = statement.executeQuery("SELECT clan1, clan2, clan1Kill, clan2Kill, winnerClan, startTime, endTime, state FROM clan_wars"))
{
while (rset.next())
{
final L2Clan attacker = getClan(rset.getInt("clan1"));
final L2Clan attacked = getClan(rset.getInt("clan2"));
if ((attacker != null) && (attacked != null))
{
final ClanWarState state = ClanWarState.values()[rset.getInt("state")];
final ClanWar clanWar = new ClanWar(attacker, attacked, rset.getInt("clan1Kill"), rset.getInt("clan2Kill"), rset.getInt("winnerClan"), rset.getLong("startTime"), rset.getLong("endTime"), state);
attacker.addWar(attacked.getId(), clanWar);
attacked.addWar(attacker.getId(), clanWar);
}
else
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Restorewars one of clans is null attacker:" + attacker + " attacked:" + attacked);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error restoring clan wars data.", e);
}
}
/**
* Check for nonexistent alliances
*/
private void allianceCheck()
{
for (L2Clan clan : _clans.values())
{
final int allyId = clan.getAllyId();
if ((allyId != 0) && (clan.getId() != allyId) && !_clans.containsKey(allyId))
{
clan.setAllyId(0);
clan.setAllyName(null);
clan.changeAllyCrest(0, true);
clan.updateClanInDB();
LOGGER.info(getClass().getSimpleName() + ": Removed alliance from clan: " + clan);
}
}
}
public List<L2Clan> getClanAllies(int allianceId)
{
final List<L2Clan> clanAllies = new ArrayList<>();
if (allianceId != 0)
{
for (L2Clan clan : _clans.values())
{
if ((clan != null) && (clan.getAllyId() == allianceId))
{
clanAllies.add(clan);
}
}
}
return clanAllies;
}
public void shutdown()
{
for (L2Clan clan : _clans.values())
{
clan.updateInDB();
}
}
public static ClanTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClanTable _instance = new ClanTable();
}
}

View File

@@ -0,0 +1,219 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.model.L2Clan;
import com.l2jmobius.gameserver.model.L2Crest;
import com.l2jmobius.gameserver.model.L2Crest.CrestType;
/**
* Loads and saves crests from database.
* @author NosBit
*/
public final class CrestTable
{
private static final Logger LOGGER = Logger.getLogger(CrestTable.class.getName());
private final Map<Integer, L2Crest> _crests = new ConcurrentHashMap<>();
private final AtomicInteger _nextId = new AtomicInteger(1);
protected CrestTable()
{
load();
}
public synchronized void load()
{
_crests.clear();
final Set<Integer> crestsInUse = new HashSet<>();
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if (clan.getCrestId() != 0)
{
crestsInUse.add(clan.getCrestId());
}
if (clan.getCrestLargeId() != 0)
{
crestsInUse.add(clan.getCrestLargeId());
}
if (clan.getAllyCrestId() != 0)
{
crestsInUse.add(clan.getAllyCrestId());
}
}
try (Connection con = DatabaseFactory.getConnection();
Statement statement = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = statement.executeQuery("SELECT `crest_id`, `data`, `type` FROM `crests` ORDER BY `crest_id` DESC"))
{
while (rs.next())
{
final int id = rs.getInt("crest_id");
if (_nextId.get() <= id)
{
_nextId.set(id + 1);
}
// delete all unused crests except the last one we dont want to reuse
// a crest id because client will display wrong crest if its reused
if (!crestsInUse.contains(id) && (id != (_nextId.get() - 1)))
{
rs.deleteRow();
continue;
}
final byte[] data = rs.getBytes("data");
final CrestType crestType = CrestType.getById(rs.getInt("type"));
if (crestType != null)
{
_crests.put(id, new L2Crest(id, data, crestType));
}
else
{
LOGGER.warning("Unknown crest type found in database. Type:" + rs.getInt("type"));
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "There was an error while loading crests from database:", e);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crests.size() + " Crests.");
for (L2Clan clan : ClanTable.getInstance().getClans())
{
if ((clan.getCrestId() != 0) && (getCrest(clan.getCrestId()) == null))
{
LOGGER.info("Removing non-existent crest for clan " + clan.getName() + " [" + clan.getId() + "], crestId:" + clan.getCrestId());
clan.setCrestId(0);
clan.changeClanCrest(0);
}
if ((clan.getCrestLargeId() != 0) && (getCrest(clan.getCrestLargeId()) == null))
{
LOGGER.info("Removing non-existent large crest for clan " + clan.getName() + " [" + clan.getId() + "], crestLargeId:" + clan.getCrestLargeId());
clan.setCrestLargeId(0);
clan.changeLargeCrest(0);
}
if ((clan.getAllyCrestId() != 0) && (getCrest(clan.getAllyCrestId()) == null))
{
LOGGER.info("Removing non-existent ally crest for clan " + clan.getName() + " [" + clan.getId() + "], allyCrestId:" + clan.getAllyCrestId());
clan.setAllyCrestId(0);
clan.changeAllyCrest(0, true);
}
}
}
/**
* @param crestId The crest id
* @return {@code L2Crest} if crest is found, {@code null} if crest was not found.
*/
public L2Crest getCrest(int crestId)
{
return _crests.get(crestId);
}
/**
* Creates a {@code L2Crest} object and inserts it in database and cache.
* @param data
* @param crestType
* @return {@code L2Crest} on success, {@code null} on failure.
*/
public L2Crest createCrest(byte[] data, CrestType crestType)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement("INSERT INTO `crests`(`crest_id`, `data`, `type`) VALUES(?, ?, ?)"))
{
final L2Crest crest = new L2Crest(_nextId.getAndIncrement(), data, crestType);
statement.setInt(1, crest.getId());
statement.setBytes(2, crest.getData());
statement.setInt(3, crest.getType().getId());
statement.executeUpdate();
_crests.put(crest.getId(), crest);
return crest;
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "There was an error while saving crest in database:", e);
}
return null;
}
/**
* Removes crest from database and cache.
* @param crestId the id of crest to be removed.
*/
public void removeCrest(int crestId)
{
_crests.remove(crestId);
// avoid removing last crest id we dont want to lose index...
// because client will display wrong crest if its reused
if (crestId == (_nextId.get() - 1))
{
return;
}
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM `crests` WHERE `crest_id` = ?"))
{
statement.setInt(1, crestId);
statement.executeUpdate();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, "There was an error while deleting crest from database:", e);
}
}
/**
* @return The next crest id.
*/
public int getNextId()
{
return _nextId.getAndIncrement();
}
public static CrestTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CrestTable _instance = new CrestTable();
}
}

View File

@@ -0,0 +1,491 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.enums.PrivateStoreType;
import com.l2jmobius.gameserver.instancemanager.PlayerCountManager;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.TradeItem;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.SellBuffHolder;
import com.l2jmobius.gameserver.network.Disconnection;
import com.l2jmobius.gameserver.network.L2GameClient;
public class OfflineTradersTable
{
private static Logger LOGGER = Logger.getLogger(OfflineTradersTable.class.getName());
// SQL DEFINITIONS
private static final String SAVE_OFFLINE_STATUS = "INSERT INTO character_offline_trade (`charId`,`time`,`type`,`title`) VALUES (?,?,?,?)";
private static final String SAVE_ITEMS = "INSERT INTO character_offline_trade_items (`charId`,`item`,`count`,`price`) VALUES (?,?,?,?)";
private static final String CLEAR_OFFLINE_TABLE = "DELETE FROM character_offline_trade";
private static final String CLEAR_OFFLINE_TABLE_PLAYER = "DELETE FROM character_offline_trade WHERE `charId`=?";
private static final String CLEAR_OFFLINE_TABLE_ITEMS = "DELETE FROM character_offline_trade_items";
private static final String CLEAR_OFFLINE_TABLE_ITEMS_PLAYER = "DELETE FROM character_offline_trade_items WHERE `charId`=?";
private static final String LOAD_OFFLINE_STATUS = "SELECT * FROM character_offline_trade";
private static final String LOAD_OFFLINE_ITEMS = "SELECT * FROM character_offline_trade_items WHERE `charId`=?";
protected OfflineTradersTable()
{
}
public void storeOffliners()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement stm1 = con.prepareStatement(CLEAR_OFFLINE_TABLE);
PreparedStatement stm2 = con.prepareStatement(CLEAR_OFFLINE_TABLE_ITEMS);
PreparedStatement stm3 = con.prepareStatement(SAVE_OFFLINE_STATUS);
PreparedStatement stm_items = con.prepareStatement(SAVE_ITEMS))
{
stm1.execute();
stm2.execute();
con.setAutoCommit(false); // avoid halfway done
for (L2PcInstance pc : L2World.getInstance().getPlayers())
{
try
{
if ((pc.getPrivateStoreType() != PrivateStoreType.NONE) && ((pc.getClient() == null) || pc.getClient().isDetached()))
{
stm3.setInt(1, pc.getObjectId()); // Char Id
stm3.setLong(2, pc.getOfflineStartTime());
stm3.setInt(3, pc.isSellingBuffs() ? PrivateStoreType.SELL_BUFFS.getId() : pc.getPrivateStoreType().getId()); // store type
String title = null;
switch (pc.getPrivateStoreType())
{
case BUY:
{
if (!Config.OFFLINE_TRADE_ENABLE)
{
continue;
}
title = pc.getBuyList().getTitle();
for (TradeItem i : pc.getBuyList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getItem().getId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
case SELL:
case PACKAGE_SELL:
{
if (!Config.OFFLINE_TRADE_ENABLE)
{
continue;
}
title = pc.getSellList().getTitle();
if (pc.isSellingBuffs())
{
for (SellBuffHolder holder : pc.getSellingBuffs())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, holder.getSkillId());
stm_items.setLong(3, 0);
stm_items.setLong(4, holder.getPrice());
stm_items.executeUpdate();
stm_items.clearParameters();
}
}
else
{
for (TradeItem i : pc.getSellList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getObjectId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.executeUpdate();
stm_items.clearParameters();
}
}
break;
}
case MANUFACTURE:
{
if (!Config.OFFLINE_CRAFT_ENABLE)
{
continue;
}
title = pc.getStoreName();
for (Entry<Integer, Long> entry : pc.getManufactureItems().entrySet())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, entry.getKey());
stm_items.setLong(3, 0);
stm_items.setLong(4, entry.getValue());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
}
stm3.setString(4, title);
stm3.executeUpdate();
stm3.clearParameters();
con.commit(); // flush
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline trader: " + pc.getObjectId() + " " + e, e);
}
}
LOGGER.info(getClass().getSimpleName() + ": Offline traders stored.");
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while saving offline traders: " + e, e);
}
}
public void restoreOfflineTraders()
{
LOGGER.info(getClass().getSimpleName() + ": Loading offline traders...");
int nTraders = 0;
try (Connection con = DatabaseFactory.getConnection();
Statement stm = con.createStatement();
ResultSet rs = stm.executeQuery(LOAD_OFFLINE_STATUS))
{
while (rs.next())
{
final long time = rs.getLong("time");
if (Config.OFFLINE_MAX_DAYS > 0)
{
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
if (cal.getTimeInMillis() <= System.currentTimeMillis())
{
continue;
}
}
final int typeId = rs.getInt("type");
boolean isSellBuff = false;
if (typeId == PrivateStoreType.SELL_BUFFS.getId())
{
isSellBuff = true;
}
final PrivateStoreType type = isSellBuff ? PrivateStoreType.PACKAGE_SELL : PrivateStoreType.findById(typeId);
if (type == null)
{
LOGGER.warning(getClass().getSimpleName() + ": PrivateStoreType with id " + rs.getInt("type") + " could not be found.");
continue;
}
if (type == PrivateStoreType.NONE)
{
continue;
}
L2PcInstance player = null;
try
{
final L2GameClient client = new L2GameClient();
client.setDetached(true);
player = L2PcInstance.load(rs.getInt("charId"));
client.setActiveChar(player);
player.setOnlineStatus(true, false);
client.setAccountName(player.getAccountNamePlayer());
player.setClient(client);
player.setOfflineStartTime(time);
if (isSellBuff)
{
player.setIsSellingBuffs(true);
}
player.spawnMe(player.getX(), player.getY(), player.getZ());
try (PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS))
{
stm_items.setInt(1, player.getObjectId());
try (ResultSet items = stm_items.executeQuery())
{
switch (type)
{
case BUY:
{
while (items.next())
{
if (player.getBuyList().addItemByItemId(items.getInt(2), items.getLong(3), items.getLong(4)) == null)
{
continue;
// throw new NullPointerException();
}
}
player.getBuyList().setTitle(rs.getString("title"));
break;
}
case SELL:
case PACKAGE_SELL:
{
if (player.isSellingBuffs())
{
while (items.next())
{
player.getSellingBuffs().add(new SellBuffHolder(items.getInt("item"), items.getLong("price")));
}
}
else
{
while (items.next())
{
if (player.getSellList().addItem(items.getInt(2), items.getLong(3), items.getLong(4)) == null)
{
continue;
// throw new NullPointerException();
}
}
}
player.getSellList().setTitle(rs.getString("title"));
player.getSellList().setPackaged(type == PrivateStoreType.PACKAGE_SELL);
break;
}
case MANUFACTURE:
{
final Map<Integer, Long> manufactureItems = new HashMap<>();
while (items.next())
{
manufactureItems.put(items.getInt(2), items.getLong(4));
}
player.setManufactureItems(manufactureItems);
player.setStoreName(rs.getString("title"));
break;
}
}
}
}
player.sitDown();
if (Config.OFFLINE_SET_NAME_COLOR)
{
player.getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
}
player.setPrivateStoreType(type);
player.setOnlineStatus(true, true);
player.restoreEffects();
player.broadcastUserInfo();
PlayerCountManager.getInstance().incOfflineTradeCount();
nTraders++;
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error loading trader: " + player, e);
if (player != null)
{
Disconnection.of(player).defaultSequence(false);
}
}
}
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + nTraders + " offline trader(s)");
if (!Config.STORE_OFFLINE_TRADE_IN_REALTIME)
{
try (Statement stm1 = con.createStatement())
{
stm1.execute(CLEAR_OFFLINE_TABLE);
stm1.execute(CLEAR_OFFLINE_TABLE_ITEMS);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while loading offline traders: ", e);
}
}
public static synchronized void onTransaction(L2PcInstance trader, boolean finished, boolean firstCall)
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement stm1 = con.prepareStatement(CLEAR_OFFLINE_TABLE_ITEMS_PLAYER);
PreparedStatement stm2 = con.prepareStatement(CLEAR_OFFLINE_TABLE_PLAYER);
PreparedStatement stm3 = con.prepareStatement(SAVE_ITEMS);
PreparedStatement stm4 = con.prepareStatement(SAVE_OFFLINE_STATUS))
{
String title = null;
stm1.setInt(1, trader.getObjectId()); // Char Id
stm1.execute();
stm1.close();
// Trade is done - clear info
if (finished)
{
stm2.setInt(1, trader.getObjectId()); // Char Id
stm2.execute();
stm2.close();
}
else
{
try
{
if ((trader.getClient() == null) || trader.getClient().isDetached())
{
switch (trader.getPrivateStoreType())
{
case BUY:
{
if (firstCall)
{
title = trader.getBuyList().getTitle();
}
for (TradeItem i : trader.getBuyList().getItems())
{
stm3.setInt(1, trader.getObjectId());
stm3.setInt(2, i.getItem().getId());
stm3.setLong(3, i.getCount());
stm3.setLong(4, i.getPrice());
stm3.executeUpdate();
stm3.clearParameters();
}
break;
}
case SELL:
case PACKAGE_SELL:
{
if (firstCall)
{
title = trader.getSellList().getTitle();
}
if (trader.isSellingBuffs())
{
for (SellBuffHolder holder : trader.getSellingBuffs())
{
stm3.setInt(1, trader.getObjectId());
stm3.setInt(2, holder.getSkillId());
stm3.setLong(3, 0);
stm3.setLong(4, holder.getPrice());
stm3.executeUpdate();
stm3.clearParameters();
}
}
else
{
for (TradeItem i : trader.getSellList().getItems())
{
stm3.setInt(1, trader.getObjectId());
stm3.setInt(2, i.getObjectId());
stm3.setLong(3, i.getCount());
stm3.setLong(4, i.getPrice());
stm3.executeUpdate();
stm3.clearParameters();
}
}
break;
}
case MANUFACTURE:
{
if (firstCall)
{
title = trader.getStoreName();
}
for (Entry<Integer, Long> entry : trader.getManufactureItems().entrySet())
{
stm3.setInt(1, trader.getObjectId());
stm3.setInt(2, entry.getKey());
stm3.setLong(3, 0);
stm3.setLong(4, entry.getValue());
stm3.executeUpdate();
stm3.clearParameters();
}
break;
}
}
stm3.close();
if (firstCall)
{
stm4.setInt(1, trader.getObjectId()); // Char Id
stm4.setLong(2, trader.getOfflineStartTime());
stm4.setInt(3, trader.isSellingBuffs() ? PrivateStoreType.SELL_BUFFS.getId() : trader.getPrivateStoreType().getId()); // store type
stm4.setString(4, title);
stm4.executeUpdate();
stm4.clearParameters();
stm4.close();
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "OfflineTradersTable[storeTradeItems()]: Error while saving offline trader: " + trader.getObjectId() + " " + e, e);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "OfflineTradersTable[storeTradeItems()]: Error while saving offline traders: " + e, e);
}
}
public static synchronized void removeTrader(int traderObjId)
{
PlayerCountManager.getInstance().decOfflineTradeCount();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement stm1 = con.prepareStatement(CLEAR_OFFLINE_TABLE_ITEMS_PLAYER);
PreparedStatement stm2 = con.prepareStatement(CLEAR_OFFLINE_TABLE_PLAYER))
{
stm1.setInt(1, traderObjId);
stm1.execute();
stm1.close();
stm2.setInt(1, traderObjId);
stm2.execute();
stm2.close();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "OfflineTradersTable[removeTrader()]: Error while removing offline trader: " + traderObjId + " " + e, e);
}
}
/**
* Gets the single instance of OfflineTradersTable.
* @return single instance of OfflineTradersTable
*/
public static OfflineTradersTable getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final OfflineTradersTable INSTANCE = new OfflineTradersTable();
}
}

View File

@@ -0,0 +1,115 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.data.xml.impl.PetDataTable;
public class PetNameTable
{
private static Logger LOGGER = Logger.getLogger(PetNameTable.class.getName());
public static PetNameTable getInstance()
{
return SingletonHolder._instance;
}
public boolean doesPetNameExist(String name, int petNpcId)
{
boolean result = true;
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT name FROM pets p, items i WHERE p.item_obj_id = i.object_id AND name=? AND i.item_id IN (?)"))
{
ps.setString(1, name);
final StringBuilder cond = new StringBuilder();
if (!cond.toString().isEmpty())
{
cond.append(", ");
}
cond.append(PetDataTable.getInstance().getPetItemsByNpc(petNpcId));
ps.setString(2, cond.toString());
try (ResultSet rs = ps.executeQuery())
{
result = rs.next();
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing petname:" + e.getMessage(), e);
}
return result;
}
public boolean isValidPetName(String name)
{
boolean result = true;
if (!isAlphaNumeric(name))
{
return result;
}
Pattern pattern;
try
{
pattern = Pattern.compile(Config.PET_NAME_TEMPLATE);
}
catch (PatternSyntaxException e) // case of illegal pattern
{
LOGGER.warning(getClass().getSimpleName() + ": Pet name pattern of config is wrong!");
pattern = Pattern.compile(".*");
}
final Matcher regexp = pattern.matcher(name);
if (!regexp.matches())
{
result = false;
}
return result;
}
private boolean isAlphaNumeric(String text)
{
boolean result = true;
final char[] chars = text.toCharArray();
for (char aChar : chars)
{
if (!Character.isLetterOrDigit(aChar))
{
result = false;
break;
}
}
return result;
}
private static class SingletonHolder
{
protected static final PetNameTable _instance = new PetNameTable();
}
}

View File

@@ -0,0 +1,92 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.sql.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* @author Nyaran
*/
public class SummonEffectsTable
{
/** Servitors **/
// Map tree
// -> key: charObjectId, value: classIndex Map
// --> key: classIndex, value: servitors Map
// ---> key: servitorSkillId, value: Effects list
private final Map<Integer, Map<Integer, Map<Integer, List<SummonEffect>>>> _servitorEffects = new HashMap<>();
public Map<Integer, Map<Integer, Map<Integer, List<SummonEffect>>>> getServitorEffectsOwner()
{
return _servitorEffects;
}
public Map<Integer, List<SummonEffect>> getServitorEffects(L2PcInstance owner)
{
final Map<Integer, Map<Integer, List<SummonEffect>>> servitorMap = _servitorEffects.get(owner.getObjectId());
if (servitorMap == null)
{
return null;
}
return servitorMap.get(owner.getClassIndex());
}
/** Pets **/
private final Map<Integer, List<SummonEffect>> _petEffects = new HashMap<>(); // key: petItemObjectId, value: Effects list
public Map<Integer, List<SummonEffect>> getPetEffects()
{
return _petEffects;
}
public static class SummonEffect
{
private final Skill _skill;
private final int _effectCurTime;
public SummonEffect(Skill skill, int effectCurTime)
{
_skill = skill;
_effectCurTime = effectCurTime;
}
public Skill getSkill()
{
return _skill;
}
public int getEffectCurTime()
{
return _effectCurTime;
}
}
public static SummonEffectsTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SummonEffectsTable _instance = new SummonEffectsTable();
}
}

View File

@@ -0,0 +1,96 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.ActionDataHolder;
import com.l2jmobius.gameserver.model.StatsSet;
/**
* @author UnAfraid
*/
public class ActionData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ActionData.class.getName());
private final Map<Integer, ActionDataHolder> _actionData = new HashMap<>();
private final Map<Integer, Integer> _actionSkillsData = new HashMap<>(); // skillId, actionId
protected ActionData()
{
load();
}
@Override
public void load()
{
_actionData.clear();
_actionSkillsData.clear();
parseDatapackFile("data/ActionData.xml");
_actionData.values().stream().filter(h -> h.getHandler().equals("PetSkillUse") || h.getHandler().equals("ServitorSkillUse")).forEach(h -> _actionSkillsData.put(h.getOptionId(), h.getId()));
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _actionData.size() + " player actions.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "action", actionNode ->
{
final ActionDataHolder holder = new ActionDataHolder(new StatsSet(parseAttributes(actionNode)));
_actionData.put(holder.getId(), holder);
}));
}
/**
* @param id
* @return the ActionDataHolder for specified id
*/
public ActionDataHolder getActionData(int id)
{
return _actionData.get(id);
}
/**
* @param skillId
* @return the actionId corresponding to the skillId or -1 if no actionId is found for the specified skill.
*/
public int getSkillActionId(int skillId)
{
return _actionSkillsData.getOrDefault(skillId, -1);
}
/**
* Gets the single instance of ActionData.
* @return single instance of ActionData
*/
public static ActionData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ActionData _instance = new ActionData();
}
}

View File

@@ -0,0 +1,355 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.L2AccessLevel;
import com.l2jmobius.gameserver.model.L2AdminCommandAccessRight;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* Loads administrator access levels and commands.
* @author UnAfraid
*/
public final class AdminData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(AdminData.class.getName());
private final Map<Integer, L2AccessLevel> _accessLevels = new HashMap<>();
private final Map<String, L2AdminCommandAccessRight> _adminCommandAccessRights = new HashMap<>();
private final Map<L2PcInstance, Boolean> _gmList = new ConcurrentHashMap<>();
private int _highestLevel = 0;
protected AdminData()
{
load();
}
@Override
public synchronized void load()
{
_accessLevels.clear();
_adminCommandAccessRights.clear();
parseDatapackFile("config/AccessLevels.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _accessLevels.size() + " Access Levels.");
parseDatapackFile("config/AdminCommands.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _adminCommandAccessRights.size() + " Access Commands.");
}
@Override
public void parseDocument(Document doc, File f)
{
NamedNodeMap attrs;
Node attr;
StatsSet set;
L2AccessLevel level;
L2AdminCommandAccessRight command;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("access".equalsIgnoreCase(d.getNodeName()))
{
set = new StatsSet();
attrs = d.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
level = new L2AccessLevel(set);
if (level.getLevel() > _highestLevel)
{
_highestLevel = level.getLevel();
}
_accessLevels.put(level.getLevel(), level);
}
else if ("admin".equalsIgnoreCase(d.getNodeName()))
{
set = new StatsSet();
attrs = d.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
command = new L2AdminCommandAccessRight(set);
_adminCommandAccessRights.put(command.getAdminCommand(), command);
}
}
}
}
}
/**
* Returns the access level by characterAccessLevel.
* @param accessLevelNum as int
* @return the access level instance by char access level
*/
public L2AccessLevel getAccessLevel(int accessLevelNum)
{
if (accessLevelNum < 0)
{
return _accessLevels.get(-1);
}
return _accessLevels.get(accessLevelNum);
}
/**
* Gets the master access level.
* @return the master access level
*/
public L2AccessLevel getMasterAccessLevel()
{
return _accessLevels.get(_highestLevel);
}
/**
* Checks for access level.
* @param id the id
* @return {@code true}, if successful, {@code false} otherwise
*/
public boolean hasAccessLevel(int id)
{
return _accessLevels.containsKey(id);
}
/**
* Checks for access.
* @param adminCommand the admin command
* @param accessLevel the access level
* @return {@code true}, if successful, {@code false} otherwise
*/
public boolean hasAccess(String adminCommand, L2AccessLevel accessLevel)
{
L2AdminCommandAccessRight acar = _adminCommandAccessRights.get(adminCommand);
if (acar == null)
{
// Trying to avoid the spam for next time when the gm would try to use the same command
if ((accessLevel.getLevel() > 0) && (accessLevel.getLevel() == _highestLevel))
{
acar = new L2AdminCommandAccessRight(adminCommand, true, accessLevel.getLevel());
_adminCommandAccessRights.put(adminCommand, acar);
LOGGER.info(getClass().getSimpleName() + ": No rights defined for admin command " + adminCommand + " auto setting accesslevel: " + accessLevel.getLevel() + " !");
}
else
{
LOGGER.info(getClass().getSimpleName() + ": No rights defined for admin command " + adminCommand + " !");
return false;
}
}
return acar.hasAccess(accessLevel);
}
/**
* Require confirm.
* @param command the command
* @return {@code true}, if the command require confirmation, {@code false} otherwise
*/
public boolean requireConfirm(String command)
{
final L2AdminCommandAccessRight acar = _adminCommandAccessRights.get(command);
if (acar == null)
{
LOGGER.info(getClass().getSimpleName() + ": No rights defined for admin command " + command + ".");
return false;
}
return acar.getRequireConfirm();
}
/**
* Gets the all GMs.
* @param includeHidden the include hidden
* @return the all GMs
*/
public List<L2PcInstance> getAllGms(boolean includeHidden)
{
final List<L2PcInstance> tmpGmList = new ArrayList<>();
for (Entry<L2PcInstance, Boolean> entry : _gmList.entrySet())
{
if (includeHidden || !entry.getValue())
{
tmpGmList.add(entry.getKey());
}
}
return tmpGmList;
}
/**
* Gets the all GM names.
* @param includeHidden the include hidden
* @return the all GM names
*/
public List<String> getAllGmNames(boolean includeHidden)
{
final List<String> tmpGmList = new ArrayList<>();
for (Entry<L2PcInstance, Boolean> entry : _gmList.entrySet())
{
if (!entry.getValue())
{
tmpGmList.add(entry.getKey().getName());
}
else if (includeHidden)
{
tmpGmList.add(entry.getKey().getName() + " (invis)");
}
}
return tmpGmList;
}
/**
* Add a L2PcInstance player to the Set _gmList.
* @param player the player
* @param hidden the hidden
*/
public void addGm(L2PcInstance player, boolean hidden)
{
_gmList.put(player, hidden);
}
/**
* Delete a GM.
* @param player the player
*/
public void deleteGm(L2PcInstance player)
{
_gmList.remove(player);
}
/**
* GM will be displayed on clients GM list.
* @param player the player
*/
public void showGm(L2PcInstance player)
{
if (_gmList.containsKey(player))
{
_gmList.put(player, false);
}
}
/**
* GM will no longer be displayed on clients GM list.
* @param player the player
*/
public void hideGm(L2PcInstance player)
{
if (_gmList.containsKey(player))
{
_gmList.put(player, true);
}
}
/**
* Checks if is GM online.
* @param includeHidden the include hidden
* @return true, if is GM online
*/
public boolean isGmOnline(boolean includeHidden)
{
for (Entry<L2PcInstance, Boolean> entry : _gmList.entrySet())
{
if (includeHidden || !entry.getValue())
{
return true;
}
}
return false;
}
/**
* Send list to player.
* @param player the player
*/
public void sendListToPlayer(L2PcInstance player)
{
if (isGmOnline(player.isGM()))
{
player.sendPacket(SystemMessageId.GM_LIST);
for (String name : getAllGmNames(player.isGM()))
{
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.GM_C1);
sm.addString(name);
player.sendPacket(sm);
}
}
else
{
player.sendPacket(SystemMessageId.THERE_ARE_NO_GMS_CURRENTLY_VISIBLE_IN_THE_PUBLIC_LIST_AS_THEY_MAY_BE_PERFORMING_OTHER_FUNCTIONS_AT_THE_MOMENT);
}
}
/**
* Broadcast to GMs.
* @param packet the packet
*/
public void broadcastToGMs(IClientOutgoingPacket packet)
{
for (L2PcInstance gm : getAllGms(true))
{
gm.sendPacket(packet);
}
}
/**
* Broadcast message to GMs.
* @param message the message
* @return the message that was broadcasted
*/
public String broadcastMessageToGMs(String message)
{
for (L2PcInstance gm : getAllGms(true))
{
gm.sendMessage(message);
}
return message;
}
/**
* Gets the single instance of AdminTable.
* @return AccessLevels: the one and only instance of this class<br>
*/
public static AdminData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AdminData _instance = new AdminData();
}
}

View File

@@ -0,0 +1,140 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.alchemy.AlchemyCraftData;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
/**
* @author Sdw
*/
public class AlchemyData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(AlchemyData.class.getName());
private final Map<Long, AlchemyCraftData> _alchemy = new HashMap<>();
protected AlchemyData()
{
load();
}
@Override
public void load()
{
_alchemy.clear();
parseDatapackFile("data/AlchemyData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _alchemy.size() + " alchemy craft skills.");
}
@Override
public void parseDocument(Document doc, File f)
{
StatsSet set;
Node att;
NamedNodeMap attrs;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("skill".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
final AlchemyCraftData alchemyCraft = new AlchemyCraftData(set);
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("ingredients".equalsIgnoreCase(c.getNodeName()))
{
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("item".equalsIgnoreCase(b.getNodeName()))
{
final int ingId = Integer.parseInt(b.getAttributes().getNamedItem("id").getNodeValue());
final int ingCount = Integer.parseInt(b.getAttributes().getNamedItem("count").getNodeValue());
alchemyCraft.addIngredient(new ItemHolder(ingId, ingCount));
}
}
}
else if ("production".equalsIgnoreCase(c.getNodeName()))
{
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("item".equalsIgnoreCase(b.getNodeName()))
{
final String type = b.getAttributes().getNamedItem("type").getNodeValue();
final int prodId = Integer.parseInt(b.getAttributes().getNamedItem("id").getNodeValue());
final int prodCount = Integer.parseInt(b.getAttributes().getNamedItem("count").getNodeValue());
if (type.equalsIgnoreCase("ON_SUCCESS"))
{
alchemyCraft.setProductionSuccess(new ItemHolder(prodId, prodCount));
}
else if (type.equalsIgnoreCase("ON_FAILURE"))
{
alchemyCraft.setProductionFailure(new ItemHolder(prodId, prodCount));
}
}
}
}
}
_alchemy.put(SkillData.getSkillHashCode(set.getInt("id"), set.getInt("level")), alchemyCraft);
}
}
}
}
}
public AlchemyCraftData getCraftData(int skillId, int skillLevel)
{
return _alchemy.get(SkillData.getSkillHashCode(skillId, skillLevel));
}
/**
* Gets the single instance of AlchemyData.
* @return single instance of AlchemyData
*/
public static AlchemyData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AlchemyData _instance = new AlchemyData();
}
}

View File

@@ -0,0 +1,164 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.AppearanceHolder;
import com.l2jmobius.gameserver.model.items.appearance.AppearanceStone;
import com.l2jmobius.gameserver.model.items.appearance.AppearanceTargetType;
import com.l2jmobius.gameserver.model.items.type.CrystalType;
/**
* @author UnAfraid
*/
public class AppearanceItemData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(AppearanceItemData.class.getName());
private final Map<Integer, AppearanceStone> _stones = new HashMap<>();
protected AppearanceItemData()
{
load();
}
@Override
public void load()
{
parseDatapackFile("data/AppearanceStones.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _stones.size() + " Stones");
//@formatter:off
/*
for (L2Item item : ItemTable.getInstance().getAllItems())
{
if ((item == null) || !item.getName().contains("Appearance Stone"))
{
continue;
}
if (item.getName().contains("Pack") || _stones.containsKey(item.getId()))
{
continue;
}
System.out.println("Unhandled appearance stone: " + item);
}
*/
//@formatter:on
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("appearance_stone".equalsIgnoreCase(d.getNodeName()))
{
final AppearanceStone stone = new AppearanceStone(new StatsSet(parseAttributes(d)));
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
switch (c.getNodeName())
{
case "grade":
{
final CrystalType type = CrystalType.valueOf(c.getTextContent());
stone.addCrystalType(type);
break;
}
case "targetType":
{
final AppearanceTargetType type = AppearanceTargetType.valueOf(c.getTextContent());
stone.addTargetType(type);
break;
}
case "bodyPart":
{
final long part = ItemTable.SLOTS.get(c.getTextContent());
stone.addBodyPart(part);
break;
}
case "race":
{
final Race race = Race.valueOf(c.getTextContent());
stone.addRace(race);
break;
}
case "raceNot":
{
final Race raceNot = Race.valueOf(c.getTextContent());
stone.addRaceNot(raceNot);
break;
}
case "visual":
{
stone.addVisualId(new AppearanceHolder(new StatsSet(parseAttributes(c))));
}
}
}
if (ItemTable.getInstance().getTemplate(stone.getId()) != null)
{
_stones.put(stone.getId(), stone);
}
else
{
LOGGER.info(getClass().getSimpleName() + ": Could not find appearance stone item " + stone.getId());
}
}
}
}
}
}
public int getLoadedElementsCount()
{
return _stones.size();
}
public AppearanceStone getStone(int stone)
{
return _stones.get(stone);
}
/**
* Gets the single instance of AppearanceItemData.
* @return single instance of AppearanceItemData
*/
public static AppearanceItemData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AppearanceItemData _instance = new AppearanceItemData();
}
}

View File

@@ -0,0 +1,188 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.L2ArmorSet;
import com.l2jmobius.gameserver.model.holders.ArmorsetSkillHolder;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.stats.BaseStats;
/**
* Loads armor set bonuses.
* @author godson, Luno, UnAfraid
*/
public final class ArmorSetsData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ArmorSetsData.class.getName());
private final Map<Integer, L2ArmorSet> _armorSets = new HashMap<>();
private final Map<Integer, List<L2ArmorSet>> _armorSetItems = new HashMap<>();
protected ArmorSetsData()
{
load();
}
@Override
public void load()
{
_armorSets.clear();
parseDatapackDirectory("data/stats/armorsets", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorSets.size() + " Armor sets.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node setNode = n.getFirstChild(); setNode != null; setNode = setNode.getNextSibling())
{
if ("set".equalsIgnoreCase(setNode.getNodeName()))
{
final int id = parseInteger(setNode.getAttributes(), "id");
final int minimumPieces = parseInteger(setNode.getAttributes(), "minimumPieces", 0);
final boolean isVisual = parseBoolean(setNode.getAttributes(), "visual", false);
final L2ArmorSet set = new L2ArmorSet(id, minimumPieces, isVisual);
if (_armorSets.putIfAbsent(id, set) != null)
{
LOGGER.warning("Duplicate set entry with id: " + id + " in file: " + f.getName());
}
for (Node innerSetNode = setNode.getFirstChild(); innerSetNode != null; innerSetNode = innerSetNode.getNextSibling())
{
switch (innerSetNode.getNodeName())
{
case "requiredItems":
{
forEach(innerSetNode, b -> "item".equals(b.getNodeName()), node ->
{
final NamedNodeMap attrs = node.getAttributes();
final int itemId = parseInteger(attrs, "id");
final L2Item item = ItemTable.getInstance().getTemplate(itemId);
if (item == null)
{
LOGGER.warning("Attempting to register non existing required item: " + itemId + " to a set: " + f.getName());
}
else if (!set.addRequiredItem(itemId))
{
LOGGER.warning("Attempting to register duplicate required item " + item + " to a set: " + f.getName());
}
});
break;
}
case "optionalItems":
{
forEach(innerSetNode, b -> "item".equals(b.getNodeName()), node ->
{
final NamedNodeMap attrs = node.getAttributes();
final int itemId = parseInteger(attrs, "id");
final L2Item item = ItemTable.getInstance().getTemplate(itemId);
if (item == null)
{
LOGGER.warning("Attempting to register non existing optional item: " + itemId + " to a set: " + f.getName());
}
else if (!set.addOptionalItem(itemId))
{
LOGGER.warning("Attempting to register duplicate optional item " + item + " to a set: " + f.getName());
}
});
break;
}
case "skills":
{
forEach(innerSetNode, b -> "skill".equals(b.getNodeName()), node ->
{
final NamedNodeMap attrs = node.getAttributes();
final int skillId = parseInteger(attrs, "id");
final int skillLevel = parseInteger(attrs, "level");
final int minPieces = parseInteger(attrs, "minimumPieces", set.getMinimumPieces());
final int minEnchant = parseInteger(attrs, "minimumEnchant", 0);
final boolean isOptional = parseBoolean(attrs, "optional", false);
final int artifactSlotMask = parseInteger(attrs, "slotMask", 0);
final int artifactBookSlot = parseInteger(attrs, "bookSlot", 0);
set.addSkill(new ArmorsetSkillHolder(skillId, skillLevel, minPieces, minEnchant, isOptional, artifactSlotMask, artifactBookSlot));
});
break;
}
case "stats":
{
forEach(innerSetNode, b -> "stat".equals(b.getNodeName()), node ->
{
final NamedNodeMap attrs = node.getAttributes();
set.addStatsBonus(parseEnum(attrs, BaseStats.class, "type"), parseInteger(attrs, "val"));
});
break;
}
}
}
Stream.concat(set.getRequiredItems().stream(), set.getOptionalItems().stream()).forEach(itemHolder -> _armorSetItems.computeIfAbsent(itemHolder, key -> new ArrayList<>()).add(set));
}
}
}
}
}
/**
* @param setId the set id that is attached to a set
* @return the armor set associated to the given item id
*/
public L2ArmorSet getSet(int setId)
{
return _armorSets.get(setId);
}
/**
* @param itemId the item id that is attached to a set
* @return the armor set associated to the given item id
*/
public List<L2ArmorSet> getSets(int itemId)
{
return _armorSetItems.getOrDefault(itemId, Collections.emptyList());
}
/**
* Gets the single instance of ArmorSetsData
* @return single instance of ArmorSetsData
*/
public static ArmorSetsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ArmorSetsData _instance = new ArmorSetsData();
}
}

View File

@@ -0,0 +1,100 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
/**
* @author Mobius
*/
public class AttendanceRewardData implements IGameXmlReader
{
private static Logger LOGGER = Logger.getLogger(AttendanceRewardData.class.getName());
private final List<ItemHolder> _rewards = new ArrayList<>();
private int _rewardsCount = 0;
protected AttendanceRewardData()
{
load();
}
@Override
public void load()
{
if (Config.ENABLE_ATTENDANCE_REWARDS)
{
_rewards.clear();
parseDatapackFile("data/AttendanceRewards.xml");
_rewardsCount = _rewards.size();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _rewardsCount + " rewards.");
}
else
{
LOGGER.info(getClass().getSimpleName() + ": Disabled.");
}
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "item", rewardNode ->
{
final StatsSet set = new StatsSet(parseAttributes(rewardNode));
final int itemId = set.getInt("id");
final int itemCount = set.getInt("count");
if (ItemTable.getInstance().getTemplate(itemId) == null)
{
LOGGER.info(getClass().getSimpleName() + ": Item with id " + itemId + " does not exist.");
}
else
{
_rewards.add(new ItemHolder(itemId, itemCount));
}
}));
}
public List<ItemHolder> getRewards()
{
return _rewards;
}
public int getRewardsCount()
{
return _rewardsCount;
}
public static AttendanceRewardData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final AttendanceRewardData _instance = new AttendanceRewardData();
}
}

View File

@@ -0,0 +1,166 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.enums.Sex;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.beautyshop.BeautyData;
import com.l2jmobius.gameserver.model.beautyshop.BeautyItem;
/**
* @author Sdw
*/
public final class BeautyShopData implements IGameXmlReader
{
private final Map<Race, Map<Sex, BeautyData>> _beautyList = new HashMap<>();
private final Map<Sex, BeautyData> _beautyData = new HashMap<>();
protected BeautyShopData()
{
load();
}
@Override
public synchronized void load()
{
_beautyList.clear();
_beautyData.clear();
parseDatapackFile("data/BeautyShop.xml");
}
@Override
public void parseDocument(Document doc, File f)
{
NamedNodeMap attrs;
StatsSet set;
Node att;
Race race = null;
Sex sex = null;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("race".equalsIgnoreCase(d.getNodeName()))
{
att = d.getAttributes().getNamedItem("type");
if (att != null)
{
race = parseEnum(att, Race.class);
}
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("sex".equalsIgnoreCase(b.getNodeName()))
{
att = b.getAttributes().getNamedItem("type");
if (att != null)
{
sex = parseEnum(att, Sex.class);
}
final BeautyData beautyData = new BeautyData();
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("hair".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
final BeautyItem hair = new BeautyItem(set);
for (Node g = a.getFirstChild(); g != null; g = g.getNextSibling())
{
if ("color".equalsIgnoreCase(g.getNodeName()))
{
attrs = g.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
hair.addColor(set);
}
}
beautyData.addHair(hair);
}
else if ("face".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
final BeautyItem face = new BeautyItem(set);
beautyData.addFace(face);
}
}
_beautyData.put(sex, beautyData);
}
}
_beautyList.put(race, _beautyData);
}
}
}
}
}
public boolean hasBeautyData(Race race, Sex sex)
{
return _beautyList.containsKey(race) && _beautyList.get(race).containsKey(sex);
}
public BeautyData getBeautyData(Race race, Sex sex)
{
if (_beautyList.containsKey(race))
{
return _beautyList.get(race).get(sex);
}
return null;
}
public static BeautyShopData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final BeautyShopData _instance = new BeautyShopData();
}
}

View File

@@ -0,0 +1,175 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.io.FileFilter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.file.filter.NumericNameFilter;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.buylist.Product;
import com.l2jmobius.gameserver.model.buylist.ProductList;
import com.l2jmobius.gameserver.model.items.L2Item;
/**
* Loads buy lists for NPCs.
* @author NosBit
*/
public final class BuyListData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(BuyListData.class.getName());
private final Map<Integer, ProductList> _buyLists = new HashMap<>();
private static final FileFilter NUMERIC_FILTER = new NumericNameFilter();
protected BuyListData()
{
load();
}
@Override
public synchronized void load()
{
_buyLists.clear();
parseDatapackDirectory("data/buylists", false);
if (Config.CUSTOM_BUYLIST_LOAD)
{
parseDatapackDirectory("data/buylists/custom", false);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _buyLists.size() + " BuyLists.");
try (Connection con = DatabaseFactory.getConnection();
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery("SELECT * FROM `buylists`"))
{
while (rs.next())
{
final int buyListId = rs.getInt("buylist_id");
final int itemId = rs.getInt("item_id");
final long count = rs.getLong("count");
final long nextRestockTime = rs.getLong("next_restock_time");
final ProductList buyList = getBuyList(buyListId);
if (buyList == null)
{
LOGGER.warning("BuyList found in database but not loaded from xml! BuyListId: " + buyListId);
continue;
}
final Product product = buyList.getProductByItemId(itemId);
if (product == null)
{
LOGGER.warning("ItemId found in database but not loaded from xml! BuyListId: " + buyListId + " ItemId: " + itemId);
continue;
}
if (count < product.getMaxCount())
{
product.setCount(count);
product.restartRestockTask(nextRestockTime);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to load buyList data from database.", e);
}
}
@Override
public void parseDocument(Document doc, File f)
{
try
{
final int buyListId = Integer.parseInt(f.getName().replaceAll(".xml", ""));
forEach(doc, "list", (list) ->
{
final int defaultBaseTax = parseInteger(list.getAttributes(), "baseTax", 0);
final ProductList buyList = new ProductList(buyListId);
forEach(list, (node) ->
{
switch (node.getNodeName())
{
case "item":
{
final NamedNodeMap attrs = node.getAttributes();
final int itemId = parseInteger(attrs, "id");
final L2Item item = ItemTable.getInstance().getTemplate(itemId);
if (item != null)
{
final long price = parseLong(attrs, "price", -1L);
final long restockDelay = parseLong(attrs, "restock_delay", -1L);
final long count = parseLong(attrs, "count", -1L);
final int baseTax = parseInteger(attrs, "baseTax", defaultBaseTax);
buyList.addProduct(new Product(buyListId, item, price, restockDelay, count, baseTax));
}
else
{
LOGGER.warning("Item not found. BuyList:" + buyListId + " ItemID:" + itemId + " File:" + f);
}
break;
}
case "npcs":
{
forEach(node, "npc", (npcNode) -> buyList.addAllowedNpc(Integer.parseInt(npcNode.getTextContent())));
break;
}
}
});
_buyLists.put(buyListId, buyList);
});
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to load buyList data from xml File:" + f.getName(), e);
}
}
@Override
public FileFilter getCurrentFileFilter()
{
return NUMERIC_FILTER;
}
public ProductList getBuyList(int listId)
{
return _buyLists.get(listId);
}
public static BuyListData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final BuyListData _instance = new BuyListData();
}
}

View File

@@ -0,0 +1,148 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.CastleSide;
import com.l2jmobius.gameserver.enums.SiegeGuardType;
import com.l2jmobius.gameserver.model.holders.CastleSpawnHolder;
import com.l2jmobius.gameserver.model.holders.SiegeGuardHolder;
/**
* @author St3eT
*/
public final class CastleData implements IGameXmlReader
{
private final Map<Integer, List<CastleSpawnHolder>> _spawns = new HashMap<>();
private static final Map<Integer, List<SiegeGuardHolder>> _siegeGuards = new HashMap<>();
protected CastleData()
{
load();
}
@Override
public void load()
{
_spawns.clear();
_siegeGuards.clear();
parseDatapackDirectory("data/residences/castles", true);
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node listNode = doc.getFirstChild(); listNode != null; listNode = listNode.getNextSibling())
{
if ("list".equals(listNode.getNodeName()))
{
for (Node castleNode = listNode.getFirstChild(); castleNode != null; castleNode = castleNode.getNextSibling())
{
if ("castle".equals(castleNode.getNodeName()))
{
final int castleId = parseInteger(castleNode.getAttributes(), "id");
for (Node tpNode = castleNode.getFirstChild(); tpNode != null; tpNode = tpNode.getNextSibling())
{
final List<CastleSpawnHolder> spawns = new ArrayList<>();
if ("spawns".equals(tpNode.getNodeName()))
{
for (Node npcNode = tpNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
if ("npc".equals(npcNode.getNodeName()))
{
final NamedNodeMap np = npcNode.getAttributes();
final int npcId = parseInteger(np, "id");
final CastleSide side = parseEnum(np, CastleSide.class, "castleSide", CastleSide.NEUTRAL);
final int x = parseInteger(np, "x");
final int y = parseInteger(np, "y");
final int z = parseInteger(np, "z");
final int heading = parseInteger(np, "heading");
spawns.add(new CastleSpawnHolder(npcId, side, x, y, z, heading));
}
}
_spawns.put(castleId, spawns);
}
else if ("siegeGuards".equals(tpNode.getNodeName()))
{
final List<SiegeGuardHolder> guards = new ArrayList<>();
for (Node npcNode = tpNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
if ("guard".equals(npcNode.getNodeName()))
{
final NamedNodeMap np = npcNode.getAttributes();
final int itemId = parseInteger(np, "itemId");
final SiegeGuardType type = parseEnum(tpNode.getAttributes(), SiegeGuardType.class, "type");
final boolean stationary = parseBoolean(np, "stationary", false);
final int npcId = parseInteger(np, "npcId");
final int npcMaxAmount = parseInteger(np, "npcMaxAmount");
guards.add(new SiegeGuardHolder(castleId, itemId, type, stationary, npcId, npcMaxAmount));
}
}
_siegeGuards.put(castleId, guards);
}
}
}
}
}
}
}
public final List<CastleSpawnHolder> getSpawnsForSide(int castleId, CastleSide side)
{
return _spawns.getOrDefault(castleId, Collections.emptyList()).stream().filter(s -> s.getSide() == side).collect(Collectors.toList());
}
public final List<SiegeGuardHolder> getSiegeGuardsForCastle(int castleId)
{
return _siegeGuards.getOrDefault(castleId, Collections.emptyList());
}
public final Map<Integer, List<SiegeGuardHolder>> getSiegeGuards()
{
return _siegeGuards;
}
/**
* Gets the single instance of CastleData.
* @return single instance of CastleData
*/
public static CastleData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CastleData _instance = new CastleData();
}
}

View File

@@ -0,0 +1,126 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.CategoryType;
/**
* Loads the category data with Class or NPC IDs.
* @author NosBit, xban1x
*/
public final class CategoryData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(CategoryData.class.getName());
private final Map<CategoryType, Set<Integer>> _categories = new HashMap<>();
protected CategoryData()
{
load();
}
@Override
public void load()
{
_categories.clear();
parseDatapackFile("data/CategoryData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _categories.size() + " Categories.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("category".equalsIgnoreCase(list_node.getNodeName()))
{
final NamedNodeMap attrs = list_node.getAttributes();
final CategoryType categoryType = CategoryType.findByName(attrs.getNamedItem("name").getNodeValue());
if (categoryType == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Can't find category by name: " + attrs.getNamedItem("name").getNodeValue());
continue;
}
final Set<Integer> ids = new HashSet<>();
for (Node category_node = list_node.getFirstChild(); category_node != null; category_node = category_node.getNextSibling())
{
if ("id".equalsIgnoreCase(category_node.getNodeName()))
{
ids.add(Integer.parseInt(category_node.getTextContent()));
}
}
_categories.put(categoryType, ids);
}
}
}
}
}
/**
* Checks if ID is in category.
* @param type The category type
* @param id The id to be checked
* @return {@code true} if id is in category, {@code false} if id is not in category or category was not found
*/
public boolean isInCategory(CategoryType type, int id)
{
final Set<Integer> category = getCategoryByType(type);
if (category == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Can't find category type: " + type);
return false;
}
return category.contains(id);
}
/**
* Gets the category by category type.
* @param type The category type
* @return A {@code Set} containing all the IDs in category if category is found, {@code null} if category was not found
*/
public Set<Integer> getCategoryByType(CategoryType type)
{
return _categories.get(type);
}
public static CategoryData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CategoryData _instance = new CategoryData();
}
}

View File

@@ -0,0 +1,212 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.ClanHallGrade;
import com.l2jmobius.gameserver.enums.ClanHallType;
import com.l2jmobius.gameserver.model.L2Clan;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.entity.ClanHall;
import com.l2jmobius.gameserver.model.holders.ClanHallTeleportHolder;
/**
* @author St3eT
*/
public final class ClanHallData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ClanHallData.class.getName());
private static final Map<Integer, ClanHall> _clanHalls = new HashMap<>();
protected ClanHallData()
{
load();
}
@Override
public void load()
{
parseDatapackDirectory("data/residences/clanHalls", true);
LOGGER.info(getClass().getSimpleName() + ": Succesfully loaded " + _clanHalls.size() + " Clan Halls.");
}
@Override
public void parseDocument(Document doc, File f)
{
final List<L2DoorInstance> doors = new ArrayList<>();
final List<Integer> npcs = new ArrayList<>();
final List<ClanHallTeleportHolder> teleports = new ArrayList<>();
final StatsSet params = new StatsSet();
for (Node listNode = doc.getFirstChild(); listNode != null; listNode = listNode.getNextSibling())
{
if ("list".equals(listNode.getNodeName()))
{
for (Node clanHallNode = listNode.getFirstChild(); clanHallNode != null; clanHallNode = clanHallNode.getNextSibling())
{
if ("clanHall".equals(clanHallNode.getNodeName()))
{
params.set("id", parseInteger(clanHallNode.getAttributes(), "id"));
params.set("name", parseString(clanHallNode.getAttributes(), "name", "None"));
params.set("grade", parseEnum(clanHallNode.getAttributes(), ClanHallGrade.class, "grade", ClanHallGrade.GRADE_NONE));
params.set("type", parseEnum(clanHallNode.getAttributes(), ClanHallType.class, "type", ClanHallType.OTHER));
for (Node tpNode = clanHallNode.getFirstChild(); tpNode != null; tpNode = tpNode.getNextSibling())
{
switch (tpNode.getNodeName())
{
case "auction":
{
final NamedNodeMap at = tpNode.getAttributes();
params.set("minBid", parseInteger(at, "minBid"));
params.set("lease", parseInteger(at, "lease"));
params.set("deposit", parseInteger(at, "deposit"));
break;
}
case "npcs":
{
for (Node npcNode = tpNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
if ("npc".equals(npcNode.getNodeName()))
{
final NamedNodeMap np = npcNode.getAttributes();
final int npcId = parseInteger(np, "id");
npcs.add(npcId);
}
}
params.set("npcList", npcs);
break;
}
case "doorlist":
{
for (Node npcNode = tpNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
if ("door".equals(npcNode.getNodeName()))
{
final NamedNodeMap np = npcNode.getAttributes();
final int doorId = parseInteger(np, "id");
final L2DoorInstance door = DoorData.getInstance().getDoor(doorId);
if (door != null)
{
doors.add(door);
}
}
}
params.set("doorList", doors);
break;
}
case "teleportList":
{
for (Node npcNode = tpNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
if ("teleport".equals(npcNode.getNodeName()))
{
final NamedNodeMap np = npcNode.getAttributes();
final int npcStringId = parseInteger(np, "npcStringId");
final int x = parseInteger(np, "x");
final int y = parseInteger(np, "y");
final int z = parseInteger(np, "z");
final int minFunctionLevel = parseInteger(np, "minFunctionLevel");
final int cost = parseInteger(np, "cost");
teleports.add(new ClanHallTeleportHolder(npcStringId, x, y, z, minFunctionLevel, cost));
}
}
params.set("teleportList", teleports);
break;
}
case "ownerRestartPoint":
{
final NamedNodeMap ol = tpNode.getAttributes();
params.set("owner_loc", new Location(parseInteger(ol, "x"), parseInteger(ol, "y"), parseInteger(ol, "z")));
break;
}
case "banishPoint":
{
final NamedNodeMap bl = tpNode.getAttributes();
params.set("banish_loc", new Location(parseInteger(bl, "x"), parseInteger(bl, "y"), parseInteger(bl, "z")));
break;
}
}
}
}
}
}
}
_clanHalls.put(params.getInt("id"), new ClanHall(params));
}
public ClanHall getClanHallById(int clanHallId)
{
return _clanHalls.get(clanHallId);
}
public Collection<ClanHall> getClanHalls()
{
return _clanHalls.values();
}
public ClanHall getClanHallByNpcId(int npcId)
{
return _clanHalls.values().stream().filter(ch -> ch.getNpcs().contains(npcId)).findFirst().orElse(null);
}
public ClanHall getClanHallByClan(L2Clan clan)
{
return _clanHalls.values().stream().filter(ch -> ch.getOwner() == clan).findFirst().orElse(null);
}
public ClanHall getClanHallByDoorId(int doorId)
{
final L2DoorInstance door = DoorData.getInstance().getDoor(doorId);
return _clanHalls.values().stream().filter(ch -> ch.getDoors().contains(door)).findFirst().orElse(null);
}
public List<ClanHall> getFreeAuctionableHall()
{
return _clanHalls.values().stream().filter(ch -> (ch.getType() == ClanHallType.AUCTIONABLE) && (ch.getOwner() == null)).sorted(Comparator.comparingInt(ClanHall::getResidenceId)).collect(Collectors.toList());
}
/**
* Gets the single instance of ClanHallData.
* @return single instance of ClanHallData
*/
public static ClanHallData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClanHallData _instance = new ClanHallData();
}
}

View File

@@ -0,0 +1,128 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.ClanMasteryHolder;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* @author Mobius
*/
public class ClanMasteryData implements IGameXmlReader
{
private static Logger LOGGER = Logger.getLogger(ClanMasteryData.class.getName());
private final List<ClanMasteryHolder> _clanMasteryData = new ArrayList<>();
protected ClanMasteryData()
{
load();
}
@Override
public void load()
{
_clanMasteryData.clear();
parseDatapackFile("data/ClanMasteryData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _clanMasteryData.size() + " clan masteries.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "clan", clanNode ->
{
final StatsSet set = new StatsSet(parseAttributes(clanNode));
final int id = set.getInt("mastery");
final int skill1Id = set.getInt("skill1Id");
final int skill1Level = set.getInt("skill1Level");
final Skill skill1 = SkillData.getInstance().getSkill(skill1Id, skill1Level);
if (skill1 == null)
{
LOGGER.info(getClass().getSimpleName() + ": Could not create clan mastery, skill id " + skill1Id + " with level " + skill1Level + " does not exist.");
return;
}
final int skill2Id = set.getInt("skill2Id", 0);
final int skill2Level = set.getInt("skill2Level", 0);
Skill skill2 = null;
if (skill2Id > 0)
{
skill2 = SkillData.getInstance().getSkill(skill2Id, skill2Level);
if (skill2 == null)
{
LOGGER.info(getClass().getSimpleName() + ": Could not create clan mastery, skill id " + skill2Id + " with level " + skill2Level + " does not exist.");
return;
}
}
final int skill3Id = set.getInt("skill3Id", 0);
final int skill3Level = set.getInt("skill3Level", 0);
Skill skill3 = null;
if (skill3Id > 0)
{
skill3 = SkillData.getInstance().getSkill(skill3Id, skill3Level);
if (skill3 == null)
{
LOGGER.info(getClass().getSimpleName() + ": Could not create clan mastery, skill id " + skill3Id + " with level " + skill3Level + " does not exist.");
return;
}
}
final int skill4Id = set.getInt("skill4Id", 0);
final int skill4Level = set.getInt("skill4Level", 0);
Skill skill4 = null;
if (skill4Id > 0)
{
skill4 = SkillData.getInstance().getSkill(skill4Id, skill4Level);
if (skill4 == null)
{
LOGGER.info(getClass().getSimpleName() + ": Could not create clan mastery, skill id " + skill4Id + " with level " + skill4Level + " does not exist.");
return;
}
}
final int clanLevel = set.getInt("clanLevel");
final int clanReputation = set.getInt("clanReputation");
final int previousMastery = set.getInt("previousMastery", 0);
final int previousMasteryAlt = set.getInt("previousMasteryAlt", 0);
_clanMasteryData.add(new ClanMasteryHolder(id, skill1, skill2, skill3, skill4, clanLevel, clanReputation, previousMastery, previousMasteryAlt));
}));
}
public List<ClanMasteryHolder> getMasteries()
{
return _clanMasteryData;
}
public static ClanMasteryData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClanMasteryData _instance = new ClanMasteryData();
}
}

View File

@@ -0,0 +1,168 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.enums.ClanRewardType;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.pledge.ClanRewardBonus;
/**
* @author UnAfraid
*/
public class ClanRewardData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ClanRewardData.class.getName());
private final Map<ClanRewardType, List<ClanRewardBonus>> _clanRewards = new ConcurrentHashMap<>();
protected ClanRewardData()
{
load();
}
@Override
public void load()
{
parseDatapackFile("config/ClanReward.xml");
for (ClanRewardType type : ClanRewardType.values())
{
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + (_clanRewards.containsKey(type) ? _clanRewards.get(type).size() : 0) + " rewards for " + type);
}
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc.getFirstChild(), IXmlReader::isNode, listNode ->
{
switch (listNode.getNodeName())
{
case "membersOnline":
{
parseMembersOnline(listNode);
break;
}
case "huntingBonus":
{
parseHuntingBonus(listNode);
break;
}
}
});
}
private void parseMembersOnline(Node node)
{
forEach(node, IXmlReader::isNode, memberNode ->
{
if ("players".equalsIgnoreCase(memberNode.getNodeName()))
{
final NamedNodeMap attrs = memberNode.getAttributes();
final int requiredAmount = parseInteger(attrs, "size");
final int level = parseInteger(attrs, "level");
final ClanRewardBonus bonus = new ClanRewardBonus(ClanRewardType.MEMBERS_ONLINE, level, requiredAmount);
forEach(memberNode, IXmlReader::isNode, skillNode ->
{
if ("skill".equalsIgnoreCase(skillNode.getNodeName()))
{
final NamedNodeMap skillAttr = skillNode.getAttributes();
final int skillId = parseInteger(skillAttr, "id");
final int skillLevel = parseInteger(skillAttr, "level");
bonus.setSkillReward(new SkillHolder(skillId, skillLevel));
}
});
_clanRewards.computeIfAbsent(bonus.getType(), key -> new ArrayList<>()).add(bonus);
}
});
}
private void parseHuntingBonus(Node node)
{
forEach(node, IXmlReader::isNode, memberNode ->
{
if ("hunting".equalsIgnoreCase(memberNode.getNodeName()))
{
final NamedNodeMap attrs = memberNode.getAttributes();
final int requiredAmount = parseInteger(attrs, "points");
final int level = parseInteger(attrs, "level");
final ClanRewardBonus bonus = new ClanRewardBonus(ClanRewardType.HUNTING_MONSTERS, level, requiredAmount);
forEach(memberNode, IXmlReader::isNode, itemsNode ->
{
if ("item".equalsIgnoreCase(itemsNode.getNodeName()))
{
final NamedNodeMap itemsAttr = itemsNode.getAttributes();
final int id = parseInteger(itemsAttr, "id");
final int count = parseInteger(itemsAttr, "count");
bonus.setItemReward(new ItemHolder(id, count));
}
});
_clanRewards.computeIfAbsent(bonus.getType(), key -> new ArrayList<>()).add(bonus);
}
});
}
public List<ClanRewardBonus> getClanRewardBonuses(ClanRewardType type)
{
return _clanRewards.get(type);
}
public ClanRewardBonus getHighestReward(ClanRewardType type)
{
ClanRewardBonus selectedBonus = null;
for (ClanRewardBonus currentBonus : _clanRewards.get(type))
{
if ((selectedBonus == null) || (selectedBonus.getLevel() < currentBonus.getLevel()))
{
selectedBonus = currentBonus;
}
}
return selectedBonus;
}
public Collection<List<ClanRewardBonus>> getClanRewardBonuses()
{
return _clanRewards.values();
}
/**
* Gets the single instance of ClanRewardData.
* @return single instance of ClanRewardData
*/
public static ClanRewardData getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final ClanRewardData INSTANCE = new ClanRewardData();
}
}

View File

@@ -0,0 +1,105 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.ClanShopProductHolder;
import com.l2jmobius.gameserver.model.items.L2Item;
/**
* @author Mobius
*/
public class ClanShopData implements IGameXmlReader
{
private static Logger LOGGER = Logger.getLogger(ClanShopData.class.getName());
private final List<ClanShopProductHolder> _clanShopProducts = new ArrayList<>();
protected ClanShopData()
{
load();
}
@Override
public void load()
{
_clanShopProducts.clear();
parseDatapackFile("config/ClanShop.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _clanShopProducts.size() + " clan shop products.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "clan", productNode ->
{
final StatsSet set = new StatsSet(parseAttributes(productNode));
final int clanLevel = set.getInt("level");
final int itemId = set.getInt("item");
final int count = set.getInt("count");
final long adena = set.getLong("adena");
final int fame = set.getInt("fame");
final L2Item item = ItemTable.getInstance().getTemplate(itemId);
if (item == null)
{
LOGGER.info(getClass().getSimpleName() + ": Could not create clan shop item " + itemId + ", it does not exist.");
}
else
{
_clanShopProducts.add(new ClanShopProductHolder(clanLevel, item, count, adena, fame));
}
}));
}
public ClanShopProductHolder getProduct(int itemId)
{
for (ClanShopProductHolder product : _clanShopProducts)
{
if (product.getTradeItem().getItem().getId() == itemId)
{
return product;
}
}
return null;
}
public List<ClanShopProductHolder> getProducts()
{
return _clanShopProducts;
}
public static ClanShopData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClanShopData _instance = new ClanShopData();
}
}

View File

@@ -0,0 +1,131 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.base.ClassInfo;
/**
* Loads the the list of classes and it's info.
* @author Zoey76
*/
public final class ClassListData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ClassListData.class.getName());
private final Map<ClassId, ClassInfo> _classData = new HashMap<>();
/**
* Instantiates a new class list data.
*/
protected ClassListData()
{
load();
}
@Override
public void load()
{
_classData.clear();
parseDatapackFile("data/stats/chars/classList.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _classData.size() + " Class data.");
}
@Override
public void parseDocument(Document doc, File f)
{
NamedNodeMap attrs;
Node attr;
ClassId classId;
String className;
ClassId parentClassId;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
attrs = d.getAttributes();
if ("class".equals(d.getNodeName()))
{
attr = attrs.getNamedItem("classId");
classId = ClassId.getClassId(parseInteger(attr));
attr = attrs.getNamedItem("name");
className = attr.getNodeValue();
attr = attrs.getNamedItem("parentClassId");
parentClassId = (attr != null) ? ClassId.getClassId(parseInteger(attr)) : null;
_classData.put(classId, new ClassInfo(classId, className, parentClassId));
}
}
}
}
}
/**
* Gets the class list.
* @return the complete class list.
*/
public Map<ClassId, ClassInfo> getClassList()
{
return _classData;
}
/**
* Gets the class info.
* @param classId the class Id.
* @return the class info related to the given {@code classId}.
*/
public ClassInfo getClass(ClassId classId)
{
return _classData.get(classId);
}
/**
* Gets the class info.
* @param classId the class Id as integer.
* @return the class info related to the given {@code classId}.
*/
public ClassInfo getClass(int classId)
{
final ClassId id = ClassId.getClassId(classId);
return (id != null) ? _classData.get(id) : null;
}
/**
* Gets the single instance of ClassListData.
* @return single instance of ClassListData
*/
public static ClassListData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ClassListData _instance = new ClassListData();
}
}

View File

@@ -0,0 +1,111 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.items.combination.CombinationItem;
import com.l2jmobius.gameserver.model.items.combination.CombinationItemReward;
import com.l2jmobius.gameserver.model.items.combination.CombinationItemType;
/**
* @author UnAfraid
*/
public class CombinationItemsData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(CombinationItemsData.class.getName());
private final List<CombinationItem> _items = new ArrayList<>();
protected CombinationItemsData()
{
load();
}
@Override
public synchronized void load()
{
_items.clear();
parseDatapackFile("data/CombinationItems.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " combinations.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "item", itemNode ->
{
final CombinationItem item = new CombinationItem(new StatsSet(parseAttributes(itemNode)));
forEach(itemNode, "reward", rewardNode ->
{
final int id = parseInteger(rewardNode.getAttributes(), "id");
final int count = parseInteger(rewardNode.getAttributes(), "count", 1);
final CombinationItemType type = parseEnum(rewardNode.getAttributes(), CombinationItemType.class, "type");
item.addReward(new CombinationItemReward(id, count, type));
if (ItemTable.getInstance().getTemplate(id) == null)
{
LOGGER.info(getClass().getSimpleName() + ": Could not find item with id " + id);
}
});
_items.add(item);
}));
}
public int getLoadedElementsCount()
{
return _items.size();
}
public List<CombinationItem> getItems()
{
return _items;
}
public CombinationItem getItemsBySlots(int firstSlot, int secondSlot)
{
return _items.stream().filter(item -> (item.getItemOne() == firstSlot) && (item.getItemTwo() == secondSlot)).findFirst().orElse(null);
}
public List<CombinationItem> getItemsByFirstSlot(int id)
{
return _items.stream().filter(item -> item.getItemOne() == id).collect(Collectors.toList());
}
public List<CombinationItem> getItemsBySecondSlot(int id)
{
return _items.stream().filter(item -> item.getItemTwo() == id).collect(Collectors.toList());
}
public static final CombinationItemsData getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final CombinationItemsData INSTANCE = new CombinationItemsData();
}
}

View File

@@ -0,0 +1,172 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2CubicTemplate;
import com.l2jmobius.gameserver.model.cubic.CubicSkill;
import com.l2jmobius.gameserver.model.cubic.ICubicConditionHolder;
import com.l2jmobius.gameserver.model.cubic.conditions.HealthCondition;
import com.l2jmobius.gameserver.model.cubic.conditions.HpCondition;
import com.l2jmobius.gameserver.model.cubic.conditions.HpCondition.HpConditionType;
import com.l2jmobius.gameserver.model.cubic.conditions.RangeCondition;
/**
* @author UnAfraid
*/
public class CubicData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(CubicData.class.getName());
private final Map<Integer, Map<Integer, L2CubicTemplate>> _cubics = new HashMap<>();
protected CubicData()
{
load();
}
@Override
public void load()
{
_cubics.clear();
parseDatapackDirectory("data/stats/cubics", true);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _cubics.size() + " cubics.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "cubic", cubicNode ->
{
parseTemplate(cubicNode, new L2CubicTemplate(new StatsSet(parseAttributes(cubicNode))));
}));
}
/**
* @param cubicNode
* @param template
*/
private void parseTemplate(Node cubicNode, L2CubicTemplate template)
{
forEach(cubicNode, IXmlReader::isNode, innerNode ->
{
switch (innerNode.getNodeName())
{
case "conditions":
{
parseConditions(innerNode, template, template);
break;
}
case "skills":
{
parseSkills(innerNode, template);
break;
}
}
});
_cubics.computeIfAbsent(template.getId(), key -> new HashMap<>()).put(template.getLevel(), template);
}
/**
* @param cubicNode
* @param template
* @param holder
*/
private void parseConditions(Node cubicNode, L2CubicTemplate template, ICubicConditionHolder holder)
{
forEach(cubicNode, IXmlReader::isNode, conditionNode ->
{
switch (conditionNode.getNodeName())
{
case "hp":
{
final HpConditionType type = parseEnum(conditionNode.getAttributes(), HpConditionType.class, "type");
final int hpPer = parseInteger(conditionNode.getAttributes(), "percent");
holder.addCondition(new HpCondition(type, hpPer));
break;
}
case "range":
{
final int range = parseInteger(conditionNode.getAttributes(), "value");
holder.addCondition(new RangeCondition(range));
break;
}
case "healthPercent":
{
final int min = parseInteger(conditionNode.getAttributes(), "min");
final int max = parseInteger(conditionNode.getAttributes(), "max");
holder.addCondition(new HealthCondition(min, max));
break;
}
default:
{
LOGGER.warning("Attempting to use not implemented condition: " + conditionNode.getNodeName() + " for cubic id: " + template.getId() + " level: " + template.getLevel());
break;
}
}
});
}
/**
* @param cubicNode
* @param template
*/
private void parseSkills(Node cubicNode, L2CubicTemplate template)
{
forEach(cubicNode, "skill", skillNode ->
{
final CubicSkill skill = new CubicSkill(new StatsSet(parseAttributes(skillNode)));
forEach(cubicNode, "conditions", conditionNode -> parseConditions(cubicNode, template, skill));
template.getSkills().add(skill);
});
}
/**
* @param id
* @param level
* @return the L2CubicTemplate for specified id and level
*/
public L2CubicTemplate getCubicTemplate(int id, int level)
{
return _cubics.getOrDefault(id, Collections.emptyMap()).get(level);
}
/**
* Gets the single instance of CubicData.
* @return single instance of CubicData
*/
public static CubicData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final CubicData _instance = new CubicData();
}
}

View File

@@ -0,0 +1,147 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.DailyMissionDataHolder;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
/**
* @author Sdw
*/
public class DailyMissionData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(DailyMissionData.class.getName());
private final Map<Integer, List<DailyMissionDataHolder>> _dailyMissionRewards = new LinkedHashMap<>();
private boolean _isAvailable;
protected DailyMissionData()
{
load();
}
@Override
public void load()
{
_dailyMissionRewards.clear();
parseDatapackFile("data/DailyMission.xml");
_isAvailable = !_dailyMissionRewards.isEmpty();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _dailyMissionRewards.size() + " one day rewards.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "reward", rewardNode ->
{
final StatsSet set = new StatsSet(parseAttributes(rewardNode));
final List<ItemHolder> items = new ArrayList<>(1);
forEach(rewardNode, "items", itemsNode -> forEach(itemsNode, "item", itemNode ->
{
final int itemId = parseInteger(itemNode.getAttributes(), "id");
final int itemCount = parseInteger(itemNode.getAttributes(), "count");
items.add(new ItemHolder(itemId, itemCount));
}));
set.set("items", items);
final List<ClassId> classRestriction = new ArrayList<>(1);
forEach(rewardNode, "classId", classRestrictionNode ->
{
classRestriction.add(ClassId.getClassId(Integer.parseInt(classRestrictionNode.getTextContent())));
});
set.set("classRestriction", classRestriction);
// Initial values in case handler doesn't exists
set.set("handler", "");
set.set("params", StatsSet.EMPTY_STATSET);
// Parse handler and parameters
forEach(rewardNode, "handler", handlerNode ->
{
set.set("handler", parseString(handlerNode.getAttributes(), "name"));
final StatsSet params = new StatsSet();
set.set("params", params);
forEach(handlerNode, "param", paramNode -> params.set(parseString(paramNode.getAttributes(), "name"), paramNode.getTextContent()));
});
final DailyMissionDataHolder holder = new DailyMissionDataHolder(set);
_dailyMissionRewards.computeIfAbsent(holder.getId(), k -> new ArrayList<>()).add(holder);
}));
}
public Collection<DailyMissionDataHolder> getDailyMissionData()
{
//@formatter:off
return _dailyMissionRewards.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
//@formatter:on
}
public Collection<DailyMissionDataHolder> getDailyMissionData(L2PcInstance player)
{
//@formatter:off
return _dailyMissionRewards.values()
.stream()
.flatMap(List::stream)
.filter(o -> o.isDisplayable(player))
.collect(Collectors.toList());
//@formatter:on
}
public Collection<DailyMissionDataHolder> getDailyMissionData(int id)
{
return _dailyMissionRewards.get(id);
}
public boolean isAvailable()
{
return _isAvailable;
}
/**
* Gets the single instance of DailyMissionData.
* @return single instance of DailyMissionData
*/
public static DailyMissionData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final DailyMissionData _instance = new DailyMissionData();
}
}

View File

@@ -0,0 +1,286 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.instancemanager.MapRegionManager;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.actor.templates.L2DoorTemplate;
import com.l2jmobius.gameserver.model.instancezone.Instance;
/**
* This class loads and hold info about doors.
* @author JIV, GodKratos, UnAfraid
*/
public final class DoorData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(DoorData.class.getName());
// Info holders
private final Map<String, Set<Integer>> _groups = new HashMap<>();
private final Map<Integer, L2DoorInstance> _doors = new HashMap<>();
private final Map<Integer, StatsSet> _templates = new HashMap<>();
private final Map<Integer, List<L2DoorInstance>> _regions = new HashMap<>();
protected DoorData()
{
load();
}
@Override
public void load()
{
_doors.clear();
_groups.clear();
_regions.clear();
parseDatapackFile("data/DoorData.xml");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "door", doorNode -> spawnDoor(parseDoor(doorNode))));
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _doors.size() + " Door Templates for " + _regions.size() + " regions.");
}
public StatsSet parseDoor(Node doorNode)
{
final StatsSet params = new StatsSet(parseAttributes(doorNode));
params.set("baseHpMax", 1); // Avoid doors without HP value created dead due to default value 0 in L2CharTemplate
forEach(doorNode, IXmlReader::isNode, innerDoorNode ->
{
final NamedNodeMap attrs = innerDoorNode.getAttributes();
if (innerDoorNode.getNodeName().equals("nodes"))
{
params.set("nodeZ", parseInteger(attrs, "nodeZ"));
final AtomicInteger count = new AtomicInteger();
forEach(innerDoorNode, IXmlReader::isNode, nodes ->
{
final NamedNodeMap nodeAttrs = nodes.getAttributes();
if ("node".equals(nodes.getNodeName()))
{
params.set("nodeX_" + count.get(), parseInteger(nodeAttrs, "x"));
params.set("nodeY_" + count.getAndIncrement(), parseInteger(nodeAttrs, "y"));
}
});
}
else if (attrs != null)
{
for (int i = 0; i < attrs.getLength(); i++)
{
final Node att = attrs.item(i);
params.set(att.getNodeName(), att.getNodeValue());
}
}
});
applyCollisions(params);
return params;
}
/**
* @param set
*/
private void applyCollisions(StatsSet set)
{
// Insert Collision data
if (set.contains("nodeX_0") && set.contains("nodeY_0") && set.contains("nodeX_1") && set.contains("nodeX_1"))
{
final int height = set.getInt("height", 150);
final int nodeX = set.getInt("nodeX_0");
final int nodeY = set.getInt("nodeY_0");
final int posX = set.getInt("nodeX_1");
final int posY = set.getInt("nodeX_1");
int collisionRadius; // (max) radius for movement checks
collisionRadius = Math.min(Math.abs(nodeX - posX), Math.abs(nodeY - posY));
if (collisionRadius < 20)
{
collisionRadius = 20;
}
set.set("collision_radius", collisionRadius);
set.set("collision_height", height);
}
}
/**
* Spawns the door, adds the group name and registers it to templates, regions and doors also inserts collisions data
* @param set
* @return
*/
public L2DoorInstance spawnDoor(StatsSet set)
{
// Create door template + door instance
final L2DoorTemplate template = new L2DoorTemplate(set);
final L2DoorInstance door = spawnDoor(template, null);
// Register the door
_templates.put(door.getId(), set);
_doors.put(door.getId(), door);
_regions.computeIfAbsent(MapRegionManager.getInstance().getMapRegionLocId(door), key -> new ArrayList<>()).add(door);
return door;
}
/**
* Spawns the door, adds the group name and registers it to templates
* @param template
* @param instance
* @return a new door instance based on provided template
*/
public L2DoorInstance spawnDoor(L2DoorTemplate template, Instance instance)
{
final L2DoorInstance door = new L2DoorInstance(template);
door.setCurrentHp(door.getMaxHp());
// Set instance world if provided
if (instance != null)
{
door.setInstance(instance);
}
// Spawn the door on the world
door.spawnMe(template.getX(), template.getY(), template.getZ());
// Register door's group
if (template.getGroupName() != null)
{
_groups.computeIfAbsent(door.getGroupName(), key -> new HashSet<>()).add(door.getId());
}
return door;
}
public StatsSet getDoorTemplate(int doorId)
{
return _templates.get(doorId);
}
public L2DoorInstance getDoor(int doorId)
{
return _doors.get(doorId);
}
public Set<Integer> getDoorsByGroup(String groupName)
{
return _groups.getOrDefault(groupName, Collections.emptySet());
}
public Collection<L2DoorInstance> getDoors()
{
return _doors.values();
}
public boolean checkIfDoorsBetween(Location start, Location end, Instance instance)
{
return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ(), instance);
}
public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz, Instance instance)
{
return checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false);
}
/**
* GodKratos: TODO: remove GeoData checks from door table and convert door nodes to Geo zones
* @param x
* @param y
* @param z
* @param tx
* @param ty
* @param tz
* @param instance
* @param doubleFaceCheck
* @return {@code boolean}
*/
public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean doubleFaceCheck)
{
final Collection<L2DoorInstance> allDoors = (instance != null) ? instance.getDoors() : _regions.get(MapRegionManager.getInstance().getMapRegionLocId(x, y));
if (allDoors == null)
{
return false;
}
for (L2DoorInstance doorInst : allDoors)
{
// check dead and open
if (doorInst.isDead() || doorInst.isOpen() || !doorInst.checkCollision() || (doorInst.getX(0) == 0))
{
continue;
}
boolean intersectFace = false;
for (int i = 0; i < 4; i++)
{
final int j = (i + 1) < 4 ? i + 1 : 0;
// lower part of the multiplier fraction, if it is 0 we avoid an error and also know that the lines are parallel
final int denominator = ((ty - y) * (doorInst.getX(i) - doorInst.getX(j))) - ((tx - x) * (doorInst.getY(i) - doorInst.getY(j)));
if (denominator == 0)
{
continue;
}
// multipliers to the equations of the lines. If they are lower than 0 or bigger than 1, we know that segments don't intersect
final float multiplier1 = (float) (((doorInst.getX(j) - doorInst.getX(i)) * (y - doorInst.getY(i))) - ((doorInst.getY(j) - doorInst.getY(i)) * (x - doorInst.getX(i)))) / denominator;
final float multiplier2 = (float) (((tx - x) * (y - doorInst.getY(i))) - ((ty - y) * (x - doorInst.getX(i)))) / denominator;
if ((multiplier1 >= 0) && (multiplier1 <= 1) && (multiplier2 >= 0) && (multiplier2 <= 1))
{
final int intersectZ = Math.round(z + (multiplier1 * (tz - z)));
// now checking if the resulting point is between door's min and max z
if ((intersectZ > doorInst.getZMin()) && (intersectZ < doorInst.getZMax()))
{
if (!doubleFaceCheck || intersectFace)
{
return true;
}
intersectFace = true;
}
}
}
}
return false;
}
public static DoorData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final DoorData _instance = new DoorData();
}
}

View File

@@ -0,0 +1,168 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.items.enchant.EnchantScroll;
import com.l2jmobius.gameserver.model.items.enchant.EnchantSupportItem;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
/**
* Loads item enchant data.
* @author UnAfraid
*/
public class EnchantItemData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EnchantItemData.class.getName());
private final Map<Integer, EnchantScroll> _scrolls = new HashMap<>();
private final Map<Integer, EnchantSupportItem> _supports = new HashMap<>();
/**
* Instantiates a new enchant item data.
*/
public EnchantItemData()
{
load();
}
@Override
public synchronized void load()
{
_scrolls.clear();
_supports.clear();
parseDatapackFile("data/EnchantItemData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _scrolls.size() + " Enchant Scrolls.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _supports.size() + " Support Items.");
}
@Override
public void parseDocument(Document doc, File f)
{
StatsSet set;
Node att;
NamedNodeMap attrs;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("enchant".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
try
{
final EnchantScroll item = new EnchantScroll(set);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("item".equalsIgnoreCase(cd.getNodeName()))
{
item.addItem(parseInteger(cd.getAttributes(), "id"));
}
}
_scrolls.put(item.getId(), item);
}
catch (NullPointerException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Unexistent enchant scroll: " + set.getString("id") + " defined in enchant data!");
}
catch (IllegalAccessError e)
{
LOGGER.warning(getClass().getSimpleName() + ": Wrong enchant scroll item type: " + set.getString("id") + " defined in enchant data!");
}
}
else if ("support".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
try
{
final EnchantSupportItem item = new EnchantSupportItem(set);
_supports.put(item.getId(), item);
}
catch (NullPointerException e)
{
LOGGER.warning(getClass().getSimpleName() + ": Unexistent enchant support item: " + set.getString("id") + " defined in enchant data!");
}
catch (IllegalAccessError e)
{
LOGGER.warning(getClass().getSimpleName() + ": Wrong enchant support item type: " + set.getString("id") + " defined in enchant data!");
}
}
}
}
}
}
/**
* Gets the enchant scroll.
* @param scroll the scroll
* @return enchant template for scroll
*/
public final EnchantScroll getEnchantScroll(L2ItemInstance scroll)
{
return _scrolls.get(scroll.getId());
}
/**
* Gets the support item.
* @param item the item
* @return enchant template for support item
*/
public final EnchantSupportItem getSupportItem(L2ItemInstance item)
{
return _supports.get(item.getId());
}
/**
* Gets the single instance of EnchantItemData.
* @return single instance of EnchantItemData
*/
public static EnchantItemData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemData _instance = new EnchantItemData();
}
}

View File

@@ -0,0 +1,169 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.holders.RangeChanceHolder;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.items.enchant.EnchantItemGroup;
import com.l2jmobius.gameserver.model.items.enchant.EnchantRateItem;
import com.l2jmobius.gameserver.model.items.enchant.EnchantScrollGroup;
import com.l2jmobius.gameserver.util.Util;
/**
* @author UnAfraid
*/
public final class EnchantItemGroupsData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EnchantItemGroupsData.class.getName());
private final Map<String, EnchantItemGroup> _itemGroups = new HashMap<>();
private final Map<Integer, EnchantScrollGroup> _scrollGroups = new HashMap<>();
protected EnchantItemGroupsData()
{
load();
}
@Override
public synchronized void load()
{
_itemGroups.clear();
_scrollGroups.clear();
parseDatapackFile("data/EnchantItemGroups.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _itemGroups.size() + " item group templates.");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _scrollGroups.size() + " scroll group templates.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("enchantRateGroup".equalsIgnoreCase(d.getNodeName()))
{
final String name = parseString(d.getAttributes(), "name");
final EnchantItemGroup group = new EnchantItemGroup(name);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("current".equalsIgnoreCase(cd.getNodeName()))
{
final String range = parseString(cd.getAttributes(), "enchant");
final double chance = parseDouble(cd.getAttributes(), "chance");
int min = -1;
int max = 0;
if (range.contains("-"))
{
final String[] split = range.split("-");
if ((split.length == 2) && Util.isDigit(split[0]) && Util.isDigit(split[1]))
{
min = Integer.parseInt(split[0]);
max = Integer.parseInt(split[1]);
}
}
else if (Util.isDigit(range))
{
min = Integer.parseInt(range);
max = min;
}
if ((min > -1) && (max > 0))
{
group.addChance(new RangeChanceHolder(min, max, chance));
}
}
}
_itemGroups.put(name, group);
}
else if ("enchantScrollGroup".equals(d.getNodeName()))
{
final int id = parseInteger(d.getAttributes(), "id");
final EnchantScrollGroup group = new EnchantScrollGroup(id);
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("enchantRate".equalsIgnoreCase(cd.getNodeName()))
{
final EnchantRateItem rateGroup = new EnchantRateItem(parseString(cd.getAttributes(), "group"));
for (Node z = cd.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("item".equals(z.getNodeName()))
{
final NamedNodeMap attrs = z.getAttributes();
if (attrs.getNamedItem("slot") != null)
{
rateGroup.addSlot(ItemTable.SLOTS.get(parseString(attrs, "slot")));
}
if (attrs.getNamedItem("magicWeapon") != null)
{
rateGroup.setMagicWeapon(parseBoolean(attrs, "magicWeapon"));
}
if (attrs.getNamedItem("id") != null)
{
rateGroup.setItemId(parseInteger(attrs, "id"));
}
}
}
group.addRateGroup(rateGroup);
}
}
_scrollGroups.put(id, group);
}
}
}
}
}
public EnchantItemGroup getItemGroup(L2Item item, int scrollGroup)
{
final EnchantScrollGroup group = _scrollGroups.get(scrollGroup);
final EnchantRateItem rateGroup = group.getRateGroup(item);
return rateGroup != null ? _itemGroups.get(rateGroup.getName()) : null;
}
public EnchantItemGroup getItemGroup(String name)
{
return _itemGroups.get(name);
}
public EnchantScrollGroup getScrollGroup(int id)
{
return _scrollGroups.get(id);
}
public static EnchantItemGroupsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemGroupsData _instance = new EnchantItemGroupsData();
}
}

View File

@@ -0,0 +1,122 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.items.type.CrystalType;
/**
* This class holds the Enchant HP Bonus Data.
* @author MrPoke, Zoey76
*/
public class EnchantItemHPBonusData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EnchantItemHPBonusData.class.getName());
private final Map<CrystalType, List<Integer>> _armorHPBonuses = new EnumMap<>(CrystalType.class);
private static final float FULL_ARMOR_MODIFIER = 1.5f; // TODO: Move it to config!
/**
* Instantiates a new enchant hp bonus data.
*/
protected EnchantItemHPBonusData()
{
load();
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("enchantHP".equalsIgnoreCase(d.getNodeName()))
{
final List<Integer> bonuses = new ArrayList<>(12);
for (Node e = d.getFirstChild(); e != null; e = e.getNextSibling())
{
if ("bonus".equalsIgnoreCase(e.getNodeName()))
{
bonuses.add(Integer.parseInt(e.getTextContent()));
}
}
_armorHPBonuses.put(parseEnum(d.getAttributes(), CrystalType.class, "grade"), bonuses);
}
}
}
}
}
@Override
public void load()
{
_armorHPBonuses.clear();
parseDatapackFile("data/stats/enchantHPBonus.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorHPBonuses.size() + " Enchant HP Bonuses.");
}
/**
* Gets the HP bonus.
* @param item the item
* @return the HP bonus
*/
public final int getHPBonus(L2ItemInstance item)
{
final List<Integer> values = _armorHPBonuses.get(item.getItem().getCrystalTypePlus());
if ((values == null) || values.isEmpty() || (item.getOlyEnchantLevel() <= 0))
{
return 0;
}
final int bonus = values.get(Math.min(item.getOlyEnchantLevel(), values.size()) - 1);
if (item.getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR)
{
return (int) (bonus * FULL_ARMOR_MODIFIER);
}
return bonus;
}
/**
* Gets the single instance of EnchantHPBonusData.
* @return single instance of EnchantHPBonusData
*/
public static EnchantItemHPBonusData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemHPBonusData _instance = new EnchantItemHPBonusData();
}
}

View File

@@ -0,0 +1,131 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.options.EnchantOptions;
import com.l2jmobius.gameserver.util.Util;
/**
* @author UnAfraid
*/
public class EnchantItemOptionsData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EnchantItemOptionsData.class.getName());
private final Map<Integer, Map<Integer, EnchantOptions>> _data = new HashMap<>();
protected EnchantItemOptionsData()
{
load();
}
@Override
public synchronized void load()
{
_data.clear();
parseDatapackFile("data/EnchantItemOptions.xml");
}
@Override
public void parseDocument(Document doc, File f)
{
int counter = 0;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("item".equalsIgnoreCase(d.getNodeName()))
{
final int itemId = parseInteger(d.getAttributes(), "id");
if (!_data.containsKey(itemId))
{
_data.put(itemId, new HashMap<>());
}
for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
if ("options".equalsIgnoreCase(cd.getNodeName()))
{
final EnchantOptions op = new EnchantOptions(parseInteger(cd.getAttributes(), "level"));
_data.get(itemId).put(op.getLevel(), op);
for (byte i = 0; i < 3; i++)
{
final Node att = cd.getAttributes().getNamedItem("option" + (i + 1));
if ((att != null) && Util.isDigit(att.getNodeValue()))
{
op.setOption(i, parseInteger(att));
}
}
counter++;
}
}
}
}
}
}
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _data.size() + " Items and " + counter + " Options.");
}
/**
* @param itemId
* @param enchantLevel
* @return enchant effects information.
*/
public EnchantOptions getOptions(int itemId, int enchantLevel)
{
if (!_data.containsKey(itemId) || !_data.get(itemId).containsKey(enchantLevel))
{
return null;
}
return _data.get(itemId).get(enchantLevel);
}
/**
* @param item
* @return enchant effects information.
*/
public EnchantOptions getOptions(L2ItemInstance item)
{
return item != null ? getOptions(item.getId(), item.getEnchantLevel()) : null;
}
/**
* Gets the single instance of EnchantOptionsData.
* @return single instance of EnchantOptionsData
*/
public static EnchantItemOptionsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantItemOptionsData _instance = new EnchantItemOptionsData();
}
}

View File

@@ -0,0 +1,143 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.SkillEnchantType;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.EnchantSkillHolder;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* This class holds the Enchant Groups information.
* @author Micr0
*/
public class EnchantSkillGroupsData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EnchantSkillGroupsData.class.getName());
private final Map<Integer, EnchantSkillHolder> _enchantSkillHolders = new LinkedHashMap<>();
private final Map<SkillHolder, Set<Integer>> _enchantSkillTrees = new HashMap<>();
public static int MAX_ENCHANT_LEVEL;
/**
* Instantiates a new enchant groups table.
*/
protected EnchantSkillGroupsData()
{
load();
}
@Override
public void load()
{
_enchantSkillHolders.clear();
parseDatapackFile("data/EnchantSkillGroups.xml");
MAX_ENCHANT_LEVEL = _enchantSkillHolders.size();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _enchantSkillHolders.size() + " enchant routes, max enchant set to " + MAX_ENCHANT_LEVEL + ".");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "enchant", enchantNode ->
{
final EnchantSkillHolder enchantSkillHolder = new EnchantSkillHolder(new StatsSet(parseAttributes(enchantNode)));
forEach(enchantNode, "sps", spsNode -> forEach(spsNode, "sp", spNode ->
{
enchantSkillHolder.addSp(parseEnum(spNode.getAttributes(), SkillEnchantType.class, "type"), parseInteger(spNode.getAttributes(), "amount"));
}));
forEach(enchantNode, "chances", chancesNode -> forEach(chancesNode, "chance", chanceNode ->
{
enchantSkillHolder.addChance(parseEnum(chanceNode.getAttributes(), SkillEnchantType.class, "type"), parseInteger(chanceNode.getAttributes(), "value"));
}));
forEach(enchantNode, "items", itemsNode -> forEach(itemsNode, "item", itemNode ->
{
enchantSkillHolder.addRequiredItem(parseEnum(itemNode.getAttributes(), SkillEnchantType.class, "type"), new ItemHolder(new StatsSet(parseAttributes(itemNode))));
}));
_enchantSkillHolders.put(parseInteger(enchantNode.getAttributes(), "level"), enchantSkillHolder);
}));
}
public void addRouteForSkill(int skillId, int level, int route)
{
addRouteForSkill(new SkillHolder(skillId, level), route);
}
public void addRouteForSkill(SkillHolder holder, int route)
{
_enchantSkillTrees.computeIfAbsent(holder, k -> new HashSet<>()).add(route);
}
public Set<Integer> getRouteForSkill(int skillId, int level)
{
return getRouteForSkill(skillId, level, 0);
}
public Set<Integer> getRouteForSkill(int skillId, int level, int subLevel)
{
return getRouteForSkill(new SkillHolder(skillId, level, subLevel));
}
public Set<Integer> getRouteForSkill(SkillHolder holder)
{
return _enchantSkillTrees.getOrDefault(holder, Collections.emptySet());
}
public boolean isEnchantable(Skill skill)
{
return isEnchantable(new SkillHolder(skill.getId(), skill.getLevel()));
}
public boolean isEnchantable(SkillHolder holder)
{
return _enchantSkillTrees.containsKey(holder);
}
public EnchantSkillHolder getEnchantSkillHolder(int level)
{
return _enchantSkillHolders.getOrDefault(level, null);
}
public static EnchantSkillGroupsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnchantSkillGroupsData _instance = new EnchantSkillGroupsData();
}
}

View File

@@ -0,0 +1,203 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.model.ensoul.EnsoulFee;
import com.l2jmobius.gameserver.model.ensoul.EnsoulOption;
import com.l2jmobius.gameserver.model.ensoul.EnsoulStone;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.items.type.CrystalType;
/**
* @author UnAfraid
*/
public class EnsoulData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EnsoulData.class.getName());
private final Map<CrystalType, EnsoulFee> _ensoulFees = new EnumMap<>(CrystalType.class);
private final Map<Integer, EnsoulOption> _ensoulOptions = new HashMap<>();
private final Map<Integer, EnsoulStone> _ensoulStones = new HashMap<>();
protected EnsoulData()
{
load();
}
@Override
public void load()
{
parseDatapackDirectory("data/stats/ensoul", true);
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _ensoulFees.size() + " fees");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _ensoulOptions.size() + " options");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _ensoulStones.size() + " stones");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, IXmlReader::isNode, ensoulNode ->
{
switch (ensoulNode.getNodeName())
{
case "fee":
{
parseFees(ensoulNode);
break;
}
case "option":
{
parseOptions(ensoulNode);
break;
}
case "stone":
{
parseStones(ensoulNode);
break;
}
}
}));
}
private void parseFees(Node ensoulNode)
{
final CrystalType type = parseEnum(ensoulNode.getAttributes(), CrystalType.class, "crystalType");
final EnsoulFee fee = new EnsoulFee(type);
forEach(ensoulNode, IXmlReader::isNode, feeNode ->
{
switch (feeNode.getNodeName())
{
case "first":
{
parseFee(feeNode, fee, 0);
break;
}
case "secondary":
{
parseFee(feeNode, fee, 1);
break;
}
case "third":
{
parseFee(feeNode, fee, 2);
break;
}
case "reNormal":
{
parseReFee(feeNode, fee, 0);
break;
}
case "reSecondary":
{
parseReFee(feeNode, fee, 1);
break;
}
case "reThird":
{
parseReFee(feeNode, fee, 2);
break;
}
}
});
}
private void parseFee(Node ensoulNode, EnsoulFee fee, int index)
{
final NamedNodeMap attrs = ensoulNode.getAttributes();
final int id = parseInteger(attrs, "itemId");
final int count = parseInteger(attrs, "count");
fee.setEnsoul(index, new ItemHolder(id, count));
_ensoulFees.put(fee.getCrystalType(), fee);
}
private void parseReFee(Node ensoulNode, EnsoulFee fee, int index)
{
final NamedNodeMap attrs = ensoulNode.getAttributes();
final int id = parseInteger(attrs, "itemId");
final int count = parseInteger(attrs, "count");
fee.setResoul(index, new ItemHolder(id, count));
}
private void parseOptions(Node ensoulNode)
{
final NamedNodeMap attrs = ensoulNode.getAttributes();
final int id = parseInteger(attrs, "id");
final String name = parseString(attrs, "name");
final String desc = parseString(attrs, "desc");
final int skillId = parseInteger(attrs, "skillId");
final int skillLevel = parseInteger(attrs, "skillLevel");
final EnsoulOption option = new EnsoulOption(id, name, desc, skillId, skillLevel);
_ensoulOptions.put(option.getId(), option);
}
private void parseStones(Node ensoulNode)
{
final NamedNodeMap attrs = ensoulNode.getAttributes();
final int id = parseInteger(attrs, "id");
final int slotType = parseInteger(attrs, "slotType");
final EnsoulStone stone = new EnsoulStone(id, slotType);
forEach(ensoulNode, "option", optionNode -> stone.addOption(parseInteger(optionNode.getAttributes(), "id")));
_ensoulStones.put(stone.getId(), stone);
}
public ItemHolder getEnsoulFee(CrystalType type, int index)
{
final EnsoulFee fee = _ensoulFees.get(type);
return fee != null ? fee.getEnsoul(index) : null;
}
public ItemHolder getResoulFee(CrystalType type, int index)
{
final EnsoulFee fee = _ensoulFees.get(type);
return fee != null ? fee.getResoul(index) : null;
}
public EnsoulOption getOption(int id)
{
return _ensoulOptions.get(id);
}
public EnsoulStone getStone(int id)
{
return _ensoulStones.get(id);
}
/**
* Gets the single instance of EnsoulData.
* @return single instance of EnsoulData
*/
public static EnsoulData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EnsoulData _instance = new EnsoulData();
}
}

View File

@@ -0,0 +1,583 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.eventengine.AbstractEventManager;
import com.l2jmobius.gameserver.model.eventengine.EventMethodNotification;
import com.l2jmobius.gameserver.model.eventengine.EventScheduler;
import com.l2jmobius.gameserver.model.eventengine.IConditionalEventScheduler;
import com.l2jmobius.gameserver.model.eventengine.conditions.BetweenConditionalScheduler;
import com.l2jmobius.gameserver.model.eventengine.conditions.HaventRunConditionalScheduler;
import com.l2jmobius.gameserver.model.eventengine.drop.EventDropGroup;
import com.l2jmobius.gameserver.model.eventengine.drop.EventDropItem;
import com.l2jmobius.gameserver.model.eventengine.drop.EventDrops;
import com.l2jmobius.gameserver.model.eventengine.drop.GroupedDrop;
import com.l2jmobius.gameserver.model.eventengine.drop.IEventDrop;
import com.l2jmobius.gameserver.model.eventengine.drop.NormalDrop;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
/**
* @author UnAfraid
*/
public final class EventEngineData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(EventEngineData.class.getName());
protected EventEngineData()
{
load();
}
@Override
public void load()
{
parseDatapackDirectory("data/events", true);
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node listNode = doc.getFirstChild(); listNode != null; listNode = listNode.getNextSibling())
{
if ("list".equals(listNode.getNodeName()))
{
for (Node eventNode = listNode.getFirstChild(); eventNode != null; eventNode = eventNode.getNextSibling())
{
if ("event".equals(eventNode.getNodeName()))
{
parseEvent(eventNode);
}
}
}
}
}
/**
* @param eventNode
*/
private void parseEvent(Node eventNode)
{
final String eventName = parseString(eventNode.getAttributes(), "name");
final String className = parseString(eventNode.getAttributes(), "class");
AbstractEventManager<?> eventManager = null;
try
{
final Class<?> clazz = Class.forName(className);
// Attempt to find getInstance() method
for (Method method : clazz.getMethods())
{
if (Modifier.isStatic(method.getModifiers()) && AbstractEventManager.class.isAssignableFrom(method.getReturnType()) && (method.getParameterCount() == 0))
{
eventManager = (AbstractEventManager<?>) method.invoke(null);
break;
}
}
if (eventManager == null)
{
throw new NoSuchMethodError("Couldn't method that gives instance of AbstractEventManager!");
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't locate event manager instance for event: " + eventName + " !", e);
return;
}
for (Node innerNode = eventNode.getFirstChild(); innerNode != null; innerNode = innerNode.getNextSibling())
{
if ("variables".equals(innerNode.getNodeName()))
{
parseVariables(eventManager, innerNode);
}
else if ("scheduler".equals(innerNode.getNodeName()))
{
parseScheduler(eventManager, innerNode);
}
else if ("rewards".equals(innerNode.getNodeName()))
{
parseRewards(eventManager, innerNode);
}
}
// Assign event name
eventManager.setName(eventName);
// Start the scheduler
eventManager.startScheduler();
// Start conditional schedulers
eventManager.startConditionalSchedulers();
// Notify the event manager that we've done initializing its stuff
eventManager.onInitialized();
LOGGER.info(getClass().getSimpleName() + ": " + eventManager.getClass().getSimpleName() + ": Initialized");
}
/**
* @param eventManager
* @param innerNode
*/
private void parseVariables(AbstractEventManager<?> eventManager, Node innerNode)
{
final StatsSet variables = new StatsSet(LinkedHashMap::new);
for (Node variableNode = innerNode.getFirstChild(); variableNode != null; variableNode = variableNode.getNextSibling())
{
if ("variable".equals(variableNode.getNodeName()))
{
variables.set(parseString(variableNode.getAttributes(), "name"), parseString(variableNode.getAttributes(), "value"));
}
else if ("list".equals(variableNode.getNodeName()))
{
parseListVariables(eventManager, variables, variableNode);
}
else if ("map".equals(variableNode.getNodeName()))
{
parseMapVariables(eventManager, variables, variableNode);
}
}
eventManager.setVariables(variables);
}
/**
* @param eventManager
* @param innerNode
*/
private void parseScheduler(AbstractEventManager<?> eventManager, Node innerNode)
{
eventManager.stopScheduler();
final Set<EventScheduler> schedulers = new LinkedHashSet<>();
final Set<IConditionalEventScheduler> conditionalSchedulers = new LinkedHashSet<>();
for (Node scheduleNode = innerNode.getFirstChild(); scheduleNode != null; scheduleNode = scheduleNode.getNextSibling())
{
if ("schedule".equals(scheduleNode.getNodeName()))
{
final StatsSet params = new StatsSet(LinkedHashMap::new);
final NamedNodeMap attrs = scheduleNode.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node node = attrs.item(i);
params.set(node.getNodeName(), node.getNodeValue());
}
final EventScheduler scheduler = new EventScheduler(eventManager, params);
for (Node eventNode = scheduleNode.getFirstChild(); eventNode != null; eventNode = eventNode.getNextSibling())
{
if ("event".equals(eventNode.getNodeName()))
{
String methodName = parseString(eventNode.getAttributes(), "name");
if (methodName.charAt(0) == '#')
{
methodName = methodName.substring(1);
}
final List<Object> args = new ArrayList<>();
for (Node argsNode = eventNode.getFirstChild(); argsNode != null; argsNode = argsNode.getNextSibling())
{
if ("arg".equals(argsNode.getNodeName()))
{
final String type = parseString(argsNode.getAttributes(), "type");
final Object value = parseObject(eventManager, type, argsNode.getTextContent());
if (value != null)
{
args.add(value);
}
}
}
try
{
scheduler.addEventNotification(new EventMethodNotification(eventManager, methodName, args));
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't add event notification for " + eventManager.getClass().getSimpleName(), e);
}
}
}
schedulers.add(scheduler);
}
else if ("conditionalSchedule".equals(scheduleNode.getNodeName()))
{
final StatsSet params = new StatsSet(LinkedHashMap::new);
final NamedNodeMap attrs = scheduleNode.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node node = attrs.item(i);
params.set(node.getNodeName(), node.getNodeValue());
}
for (Node eventNode = scheduleNode.getFirstChild(); eventNode != null; eventNode = eventNode.getNextSibling())
{
if ("run".equals(eventNode.getNodeName()))
{
final String name = parseString(eventNode.getAttributes(), "name");
final String ifType = parseString(eventNode.getAttributes(), "if", "BETWEEN").toUpperCase();
switch (ifType)
{
case "BETWEEN":
{
final List<String> names = new ArrayList<>(2);
for (Node innerData = eventNode.getFirstChild(); innerData != null; innerData = innerData.getNextSibling())
{
if ("name".equals(innerData.getNodeName()))
{
names.add(innerData.getTextContent());
}
}
if (names.size() != 2)
{
LOGGER.warning(getClass().getSimpleName() + ": Event: " + eventManager.getClass().getSimpleName() + " has incorrect amount of scheduler names: " + names + " expected: 2 found: " + names.size());
}
else
{
conditionalSchedulers.add(new BetweenConditionalScheduler(eventManager, name, names.get(0), names.get(1)));
}
break;
}
case "HAVENT_RUN":
{
conditionalSchedulers.add(new HaventRunConditionalScheduler(eventManager, name));
break;
}
}
}
}
}
}
eventManager.setSchedulers(schedulers);
eventManager.setConditionalSchedulers(conditionalSchedulers);
}
/**
* @param eventManager
* @param innerNode
*/
private void parseRewards(AbstractEventManager<?> eventManager, Node innerNode)
{
final Map<String, IEventDrop> rewards = new LinkedHashMap<>();
forEach(innerNode, IXmlReader::isNode, rewardsNode ->
{
if ("reward".equalsIgnoreCase(rewardsNode.getNodeName()))
{
final String name = parseString(rewardsNode.getAttributes(), "name");
final EventDrops dropType = parseEnum(rewardsNode.getAttributes(), EventDrops.class, "type");
switch (dropType)
{
case GROUPED:
{
final GroupedDrop droplist = dropType.newInstance();
forEach(rewardsNode, "group", groupsNode ->
{
final EventDropGroup group = new EventDropGroup(parseDouble(groupsNode.getAttributes(), "chance"));
forEach(groupsNode, "item", itemNode ->
{
final NamedNodeMap attrs = itemNode.getAttributes();
final int id = parseInteger(attrs, "id");
final int min = parseInteger(attrs, "min");
final int max = parseInteger(attrs, "max");
final double chance = parseDouble(attrs, "chance");
group.addItem(new EventDropItem(id, min, max, chance));
});
});
rewards.put(name, droplist);
break;
}
case NORMAL:
{
final NormalDrop droplist = dropType.newInstance();
forEach(rewardsNode, "item", itemNode ->
{
final NamedNodeMap attrs = itemNode.getAttributes();
final int id = parseInteger(attrs, "id");
final int min = parseInteger(attrs, "min");
final int max = parseInteger(attrs, "max");
final double chance = parseDouble(attrs, "chance");
droplist.addItem(new EventDropItem(id, min, max, chance));
});
rewards.put(name, droplist);
break;
}
}
}
});
eventManager.setRewards(rewards);
}
/**
* @param eventManager
* @param variables
* @param variableNode
*/
@SuppressWarnings("unchecked")
private void parseListVariables(AbstractEventManager<?> eventManager, StatsSet variables, Node variableNode)
{
final String name = parseString(variableNode.getAttributes(), "name");
final String type = parseString(variableNode.getAttributes(), "type");
final Class<?> classType = getClassByName(eventManager, type);
final List<?> values = newList(classType);
switch (type)
{
case "Byte":
case "Short":
case "Integer":
case "Float":
case "Long":
case "Double":
case "String":
{
for (Node stringNode = variableNode.getFirstChild(); stringNode != null; stringNode = stringNode.getNextSibling())
{
if ("value".equals(stringNode.getNodeName()))
{
((List<Object>) values).add(parseObject(eventManager, type, stringNode.getTextContent()));
}
}
break;
}
case "ItemHolder":
{
for (Node stringNode = variableNode.getFirstChild(); stringNode != null; stringNode = stringNode.getNextSibling())
{
if ("item".equals(stringNode.getNodeName()))
{
((List<ItemHolder>) values).add(new ItemHolder(parseInteger(stringNode.getAttributes(), "id"), parseLong(stringNode.getAttributes(), "count", 1L)));
}
}
break;
}
case "SkillHolder":
{
for (Node stringNode = variableNode.getFirstChild(); stringNode != null; stringNode = stringNode.getNextSibling())
{
if ("skill".equals(stringNode.getNodeName()))
{
((List<SkillHolder>) values).add(new SkillHolder(parseInteger(stringNode.getAttributes(), "id"), parseInteger(stringNode.getAttributes(), "level", 1)));
}
}
break;
}
case "Location":
{
for (Node stringNode = variableNode.getFirstChild(); stringNode != null; stringNode = stringNode.getNextSibling())
{
if ("location".equals(stringNode.getNodeName()))
{
((List<Location>) values).add(new Location(parseInteger(stringNode.getAttributes(), "x"), parseInteger(stringNode.getAttributes(), "y"), parseInteger(stringNode.getAttributes(), "z", parseInteger(stringNode.getAttributes(), "heading", 0))));
}
}
break;
}
default:
{
LOGGER.info(getClass().getSimpleName() + ": Unhandled list case: " + type + " for event: " + eventManager.getClass().getSimpleName());
break;
}
}
variables.set(name, values);
}
/**
* @param eventManager
* @param variables
* @param variableNode
*/
@SuppressWarnings("unchecked")
private void parseMapVariables(AbstractEventManager<?> eventManager, StatsSet variables, Node variableNode)
{
final String name = parseString(variableNode.getAttributes(), "name");
final String keyType = parseString(variableNode.getAttributes(), "keyType");
final String valueType = parseString(variableNode.getAttributes(), "valueType");
final Class<?> keyClass = getClassByName(eventManager, keyType);
final Class<?> valueClass = getClassByName(eventManager, valueType);
final Map<?, ?> map = newMap(keyClass, valueClass);
forEach(variableNode, IXmlReader::isNode, stringNode ->
{
switch (stringNode.getNodeName())
{
case "entry":
{
final NamedNodeMap attrs = stringNode.getAttributes();
((Map<Object, Object>) map).put(parseObject(eventManager, keyType, parseString(attrs, "key")), parseObject(eventManager, valueType, parseString(attrs, "value")));
break;
}
case "item":
{
final NamedNodeMap attrs = stringNode.getAttributes();
((Map<Object, ItemHolder>) map).put(parseObject(eventManager, keyType, parseString(attrs, "key")), new ItemHolder(parseInteger(stringNode.getAttributes(), "id"), parseLong(stringNode.getAttributes(), "count")));
break;
}
case "skill":
{
final NamedNodeMap attrs = stringNode.getAttributes();
((Map<Object, SkillHolder>) map).put(parseObject(eventManager, keyType, parseString(attrs, "key")), new SkillHolder(parseInteger(stringNode.getAttributes(), "id"), parseInteger(stringNode.getAttributes(), "level")));
break;
}
case "location":
{
final NamedNodeMap attrs = stringNode.getAttributes();
((Map<Object, Location>) map).put(parseObject(eventManager, keyType, parseString(attrs, "key")), new Location(parseInteger(stringNode.getAttributes(), "x"), parseInteger(stringNode.getAttributes(), "y"), parseInteger(stringNode.getAttributes(), "z", parseInteger(stringNode.getAttributes(), "heading", 0))));
break;
}
default:
{
LOGGER.warning(getClass().getSimpleName() + ": Unhandled map case: " + name + " " + stringNode.getNodeName() + " for event: " + eventManager.getClass().getSimpleName());
}
}
});
variables.set(name, map);
}
private Class<?> getClassByName(AbstractEventManager<?> eventManager, String name)
{
switch (name)
{
case "Byte":
{
return Byte.class;
}
case "Short":
{
return Short.class;
}
case "Integer":
{
return Integer.class;
}
case "Float":
{
return Float.class;
}
case "Long":
{
return Long.class;
}
case "Double":
{
return Double.class;
}
case "String":
{
return String.class;
}
case "ItemHolder":
{
return ItemHolder.class;
}
case "SkillHolder":
{
return SkillHolder.class;
}
case "Location":
{
return Location.class;
}
default:
{
LOGGER.warning(getClass().getSimpleName() + ": Unhandled class case: " + name + " for event: " + eventManager.getClass().getSimpleName());
return Object.class;
}
}
}
private Object parseObject(AbstractEventManager<?> eventManager, String type, String value)
{
switch (type)
{
case "Byte":
{
return Byte.decode(value);
}
case "Short":
{
return Short.decode(value);
}
case "Integer":
{
return Integer.decode(value);
}
case "Float":
{
return Float.parseFloat(value);
}
case "Long":
{
return Long.decode(value);
}
case "Double":
{
return Double.parseDouble(value);
}
case "String":
{
return value;
}
default:
{
LOGGER.warning(getClass().getSimpleName() + ": Unhandled object case: " + type + " for event: " + eventManager.getClass().getSimpleName());
return null;
}
}
}
private static <T> List<T> newList(Class<T> type)
{
return new ArrayList<>();
}
private static <K, V> Map<K, V> newMap(Class<K> keyClass, Class<V> valueClass)
{
return new LinkedHashMap<>();
}
/**
* Gets the single instance of EventEngineData.
* @return single instance of EventEngineData
*/
public static EventEngineData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EventEngineData _instance = new EventEngineData();
}
}

View File

@@ -0,0 +1,153 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
/**
* This class holds the Experience points for each level for players and pets.
* @author mrTJO
*/
public final class ExperienceData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ExperienceData.class.getName());
private final Map<Integer, Long> _expTable = new HashMap<>();
private final Map<Integer, Double> _traningRateTable = new HashMap<>();
private byte MAX_LEVEL;
private byte MAX_PET_LEVEL;
/**
* Instantiates a new experience table.
*/
protected ExperienceData()
{
load();
}
@Override
public void load()
{
_expTable.clear();
_traningRateTable.clear();
parseDatapackFile("data/stats/experience.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _expTable.size() + " levels.");
LOGGER.info(getClass().getSimpleName() + ": Max Player Level is: " + (MAX_LEVEL - 1));
LOGGER.info(getClass().getSimpleName() + ": Max Pet Level is: " + (MAX_PET_LEVEL - 1));
}
@Override
public void parseDocument(Document doc, File f)
{
final Node table = doc.getFirstChild();
final NamedNodeMap tableAttr = table.getAttributes();
MAX_LEVEL = (byte) (Byte.parseByte(tableAttr.getNamedItem("maxLevel").getNodeValue()) + 1);
MAX_PET_LEVEL = (byte) (Byte.parseByte(tableAttr.getNamedItem("maxPetLevel").getNodeValue()) + 1);
if (MAX_LEVEL > Config.PLAYER_MAXIMUM_LEVEL)
{
MAX_LEVEL = Config.PLAYER_MAXIMUM_LEVEL;
}
if (MAX_PET_LEVEL > MAX_LEVEL)
{
MAX_PET_LEVEL = MAX_LEVEL; // Pet level should not exceed owner level.
}
int maxLevel = 0;
for (Node n = table.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("experience".equals(n.getNodeName()))
{
final NamedNodeMap attrs = n.getAttributes();
maxLevel = parseInteger(attrs, "level");
if (maxLevel > Config.PLAYER_MAXIMUM_LEVEL)
{
break;
}
_expTable.put(maxLevel, parseLong(attrs, "tolevel"));
_traningRateTable.put(maxLevel, parseDouble(attrs, "trainingRate"));
}
}
}
/**
* Gets the exp for level.
* @param level the level required.
* @return the experience points required to reach the given level.
*/
public long getExpForLevel(int level)
{
if (level > Config.PLAYER_MAXIMUM_LEVEL)
{
level = Config.PLAYER_MAXIMUM_LEVEL;
}
return _expTable.get(level);
}
public double getTrainingRate(int level)
{
if (level > Config.PLAYER_MAXIMUM_LEVEL)
{
level = Config.PLAYER_MAXIMUM_LEVEL;
}
return _traningRateTable.get(level);
}
/**
* Gets the max level.
* @return the maximum level acquirable by a player.
*/
public byte getMaxLevel()
{
return MAX_LEVEL;
}
/**
* Gets the max pet level.
* @return the maximum level acquirable by a pet.
*/
public byte getMaxPetLevel()
{
return MAX_PET_LEVEL;
}
/**
* Gets the single instance of ExperienceTable.
* @return single instance of ExperienceTable
*/
public static ExperienceData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ExperienceData _instance = new ExperienceData();
}
}

View File

@@ -0,0 +1,205 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.handler.ConditionHandler;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.conditions.ICondition;
import com.l2jmobius.gameserver.model.holders.ExtendDropDataHolder;
import com.l2jmobius.gameserver.model.holders.ExtendDropItemHolder;
import com.l2jmobius.gameserver.network.SystemMessageId;
/**
* @author Sdw
*/
public class ExtendDropData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ExtendDropData.class.getName());
private final Map<Integer, ExtendDropDataHolder> _extendDrop = new HashMap<>();
protected ExtendDropData()
{
load();
}
@Override
public void load()
{
_extendDrop.clear();
parseDatapackFile("data/ExtendDrop.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _extendDrop.size() + " ExtendDrop.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "drop", dropNode ->
{
final StatsSet set = new StatsSet(parseAttributes(dropNode));
final List<ExtendDropItemHolder> items = new ArrayList<>(1);
forEach(dropNode, "items", itemsNode -> forEach(itemsNode, "item", itemNode ->
{
final int itemId = parseInteger(itemNode.getAttributes(), "id");
final int itemCount = parseInteger(itemNode.getAttributes(), "count");
final int itemMaxCount = parseInteger(itemNode.getAttributes(), "maxCount");
final double itemChance = parseDouble(itemNode.getAttributes(), "chance");
final double itemAdditionalChance = parseDouble(itemNode.getAttributes(), "additionalChance");
items.add(new ExtendDropItemHolder(itemId, itemCount, itemMaxCount, itemChance, itemAdditionalChance));
}));
set.set("items", items);
final List<ICondition> conditions = new ArrayList<>(1);
forEach(dropNode, "conditions", conditionsNode -> forEach(conditionsNode, "condition", conditionNode ->
{
final String conditionName = parseString(conditionNode.getAttributes(), "name");
final StatsSet params = (StatsSet) parseValue(conditionNode);
final Function<StatsSet, ICondition> conditionFunction = ConditionHandler.getInstance().getHandlerFactory(conditionName);
if (conditionFunction != null)
{
conditions.add(conditionFunction.apply(params));
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Missing condition for ExtendDrop Id[" + set.getInt("id") + "] Condition Name[" + conditionName + "]");
}
}));
set.set("conditions", conditions);
final Map<Long, SystemMessageId> systemMessages = new HashMap<>();
forEach(dropNode, "systemMessages", systemMessagesNode -> forEach(systemMessagesNode, "systemMessage", systemMessageNode ->
{
final long amount = parseLong(systemMessageNode.getAttributes(), "amount");
final SystemMessageId systemMessageId = SystemMessageId.getSystemMessageId(parseInteger(systemMessageNode.getAttributes(), "id"));
systemMessages.put(amount, systemMessageId);
}));
set.set("systemMessages", systemMessages);
_extendDrop.put(set.getInt("id"), new ExtendDropDataHolder(set));
}));
}
private Object parseValue(Node node)
{
StatsSet statsSet = null;
List<Object> list = null;
Object text = null;
for (node = node.getFirstChild(); node != null; node = node.getNextSibling())
{
final String nodeName = node.getNodeName();
switch (node.getNodeName())
{
case "#text":
{
final String value = node.getNodeValue().trim();
if (!value.isEmpty())
{
text = value;
}
break;
}
case "item":
{
if (list == null)
{
list = new LinkedList<>();
}
final Object value = parseValue(node);
if (value != null)
{
list.add(value);
}
break;
}
default:
{
final Object value = parseValue(node);
if (value != null)
{
if (statsSet == null)
{
statsSet = new StatsSet();
}
statsSet.set(nodeName, value);
}
}
}
}
if (list != null)
{
if (text != null)
{
throw new IllegalArgumentException("Text and list in same node are not allowed. Node[" + node + "]");
}
if (statsSet != null)
{
statsSet.set(".", list);
}
else
{
return list;
}
}
if (text != null)
{
if (list != null)
{
throw new IllegalArgumentException("Text and list in same node are not allowed. Node[" + node + "]");
}
if (statsSet != null)
{
statsSet.set(".", text);
}
else
{
return text;
}
}
return statsSet;
}
public ExtendDropDataHolder getExtendDropById(int id)
{
return _extendDrop.getOrDefault(id, null);
}
public static ExtendDropData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ExtendDropData _instance = new ExtendDropData();
}
}

View File

@@ -0,0 +1,125 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.data.sql.impl.CharNameTable;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.holders.FakePlayerHolder;
/**
* @author Mobius
*/
public class FakePlayerData implements IGameXmlReader
{
private static Logger LOGGER = Logger.getLogger(FakePlayerData.class.getName());
private final Map<Integer, FakePlayerHolder> _fakePlayerInfos = new HashMap<>();
private final Map<String, String> _fakePlayerNames = new HashMap<>();
private final Map<String, Integer> _fakePlayerIds = new HashMap<>();
private final List<String> _talkableFakePlayerNames = new ArrayList<>();
protected FakePlayerData()
{
load();
}
@Override
public void load()
{
if (Config.FAKE_PLAYERS_ENABLED)
{
_fakePlayerInfos.clear();
_fakePlayerNames.clear();
_fakePlayerIds.clear();
_talkableFakePlayerNames.clear();
parseDatapackFile("data/FakePlayerVisualData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _fakePlayerInfos.size() + " templates.");
}
else
{
LOGGER.info(getClass().getSimpleName() + ": Disabled.");
}
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "fakePlayer", fakePlayerNode ->
{
final StatsSet set = new StatsSet(parseAttributes(fakePlayerNode));
final int npcId = set.getInt("npcId");
final L2NpcTemplate template = NpcData.getInstance().getTemplate(npcId);
final String name = template.getName();
if (CharNameTable.getInstance().getIdByName(name) > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Could not create fake player template " + npcId + ", player name already exists.");
}
else
{
_fakePlayerIds.put(name, npcId); // name - npcId
_fakePlayerNames.put(name.toLowerCase(), name); // name to lowercase - name
_fakePlayerInfos.put(npcId, new FakePlayerHolder(set));
if (template.isFakePlayerTalkable())
{
_talkableFakePlayerNames.add(name.toLowerCase());
}
}
}));
}
public int getNpcIdByName(String name)
{
return _fakePlayerIds.get(name);
}
public String getProperName(String name)
{
return _fakePlayerNames.get(name.toLowerCase());
}
public Boolean isTalkable(String name)
{
return _talkableFakePlayerNames.contains(name.toLowerCase());
}
public FakePlayerHolder getInfo(int npcId)
{
return _fakePlayerInfos.get(npcId);
}
public static FakePlayerData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final FakePlayerData _instance = new FakePlayerData();
}
}

View File

@@ -0,0 +1,240 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.FenceState;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.L2WorldRegion;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2FenceInstance;
import com.l2jmobius.gameserver.model.instancezone.Instance;
/**
* @author HoridoJoho / FBIagent
*/
public final class FenceData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(FenceData.class.getSimpleName());
private static final int MAX_Z_DIFF = 100;
private final Map<L2WorldRegion, List<L2FenceInstance>> _regions = new ConcurrentHashMap<>();
private final Map<Integer, L2FenceInstance> _fences = new ConcurrentHashMap<>();
protected FenceData()
{
load();
}
@Override
public void load()
{
if (!_fences.isEmpty())
{
// Remove old fences when reloading
_fences.values().forEach(this::removeFence);
}
parseDatapackFile("data/FenceData.xml");
LOGGER.info("Loaded " + _fences.size() + " Fences.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "fence", this::spawnFence));
}
public int getLoadedElementsCount()
{
return _fences.size();
}
private void spawnFence(Node fenceNode)
{
final StatsSet set = new StatsSet(parseAttributes(fenceNode));
spawnFence(set.getInt("x"), set.getInt("y"), set.getInt("z"), set.getString("name"), set.getInt("width"), set.getInt("length"), set.getInt("height"), 0, set.getEnum("state", FenceState.class, FenceState.CLOSED));
}
public L2FenceInstance spawnFence(int x, int y, int z, int width, int length, int height, int instanceId, FenceState state)
{
return spawnFence(x, y, z, null, width, length, height, instanceId, state);
}
public L2FenceInstance spawnFence(int x, int y, int z, String name, int width, int length, int height, int instanceId, FenceState state)
{
final L2FenceInstance fence = new L2FenceInstance(x, y, name, width, length, height, state);
if (instanceId > 0)
{
fence.setInstanceById(instanceId);
}
fence.spawnMe(x, y, z);
addFence(fence);
return fence;
}
private void addFence(L2FenceInstance fence)
{
_fences.put(fence.getObjectId(), fence);
_regions.computeIfAbsent(L2World.getInstance().getRegion(fence), key -> new ArrayList<>()).add(fence);
}
public void removeFence(L2FenceInstance fence)
{
_fences.remove(fence.getObjectId());
final List<L2FenceInstance> fencesInRegion = _regions.get(L2World.getInstance().getRegion(fence));
if (fencesInRegion != null)
{
fencesInRegion.remove(fence);
}
}
public Map<Integer, L2FenceInstance> getFences()
{
return _fences;
}
public L2FenceInstance getFence(int objectId)
{
return _fences.get(objectId);
}
public boolean checkIfFenceBetween(int x, int y, int z, int tx, int ty, int tz, Instance instance)
{
final Predicate<L2FenceInstance> filter = fence ->
{
// Check if fence is geodata enabled.
if (!fence.getState().isGeodataEnabled())
{
return false;
}
// Check if fence is within the instance we search for.
final int instanceId = (instance == null) ? 0 : instance.getId();
if (fence.getInstanceId() != instanceId)
{
return false;
}
final int xMin = fence.getXMin();
final int xMax = fence.getXMax();
final int yMin = fence.getYMin();
final int yMax = fence.getYMax();
if ((x < xMin) && (tx < xMin))
{
return false;
}
if ((x > xMax) && (tx > xMax))
{
return false;
}
if ((y < yMin) && (ty < yMin))
{
return false;
}
if ((y > yMax) && (ty > yMax))
{
return false;
}
if ((x > xMin) && (tx > xMin) && (x < xMax) && (tx < xMax))
{
if ((y > yMin) && (ty > yMin) && (y < yMax) && (ty < yMax))
{
return false;
}
}
if (crossLinePart(xMin, yMin, xMax, yMin, x, y, tx, ty, xMin, yMin, xMax, yMax) || crossLinePart(xMax, yMin, xMax, yMax, x, y, tx, ty, xMin, yMin, xMax, yMax) || crossLinePart(xMax, yMax, xMin, yMax, x, y, tx, ty, xMin, yMin, xMax, yMax) || crossLinePart(xMin, yMax, xMin, yMin, x, y, tx, ty, xMin, yMin, xMax, yMax))
{
if ((z > (fence.getZ() - MAX_Z_DIFF)) && (z < (fence.getZ() + MAX_Z_DIFF)))
{
return true;
}
}
return false;
};
final L2WorldRegion region = L2World.getInstance().getRegion(x, y); // Should never be null.
return region == null ? false : _regions.getOrDefault(region, Collections.emptyList()).stream().anyMatch(filter);
}
private boolean crossLinePart(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, double xMin, double yMin, double xMax, double yMax)
{
final double[] result = intersection(x1, y1, x2, y2, x3, y3, x4, y4);
if (result == null)
{
return false;
}
final double xCross = result[0];
final double yCross = result[1];
if ((xCross <= xMax) && (xCross >= xMin))
{
return true;
}
if ((yCross <= yMax) && (yCross >= yMin))
{
return true;
}
return false;
}
private double[] intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
final double d = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
if (d == 0)
{
return null;
}
final double xi = (((x3 - x4) * ((x1 * y2) - (y1 * x2))) - ((x1 - x2) * ((x3 * y4) - (y3 * x4)))) / d;
final double yi = (((y3 - y4) * ((x1 * y2) - (y1 * x2))) - ((y1 - y2) * ((x3 * y4) - (y3 * x4)))) / d;
return new double[]
{
xi,
yi
};
}
public static FenceData getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final FenceData INSTANCE = new FenceData();
}
}

View File

@@ -0,0 +1,179 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.FishingBaitData;
/**
* This class holds the Fishing information.
* @author bit
*/
public final class FishingData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(FishingData.class.getName());
private final Map<Integer, FishingBaitData> _baitData = new HashMap<>();
private int _baitDistanceMin;
private int _baitDistanceMax;
private double _expRateMin;
private double _expRateMax;
private double _spRateMin;
private double _spRateMax;
/**
* Instantiates a new fishing data.
*/
protected FishingData()
{
load();
}
@Override
public void load()
{
_baitData.clear();
parseDatapackFile("data/Fishing.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded Fishing Data.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node listItem = n.getFirstChild(); listItem != null; listItem = listItem.getNextSibling())
{
switch (listItem.getNodeName())
{
case "baitDistance":
{
_baitDistanceMin = parseInteger(listItem.getAttributes(), "min");
_baitDistanceMax = parseInteger(listItem.getAttributes(), "max");
break;
}
case "experienceRate":
{
_expRateMin = parseDouble(listItem.getAttributes(), "min");
_expRateMax = parseDouble(listItem.getAttributes(), "max");
break;
}
case "skillPointsRate":
{
_spRateMin = parseDouble(listItem.getAttributes(), "min");
_spRateMax = parseDouble(listItem.getAttributes(), "max");
break;
}
case "baits":
{
for (Node bait = listItem.getFirstChild(); bait != null; bait = bait.getNextSibling())
{
if ("bait".equalsIgnoreCase(bait.getNodeName()))
{
final NamedNodeMap attrs = bait.getAttributes();
final int itemId = parseInteger(attrs, "itemId");
final int level = parseInteger(attrs, "level");
final int minPlayerLevel = parseInteger(attrs, "minPlayerLevel");
final double chance = parseDouble(attrs, "chance");
final int timeMin = parseInteger(attrs, "timeMin");
final int timeMax = parseInteger(attrs, "timeMax");
final int waitMin = parseInteger(attrs, "waitMin");
final int waitMax = parseInteger(attrs, "waitMax");
final FishingBaitData baitData = new FishingBaitData(itemId, level, minPlayerLevel, chance, timeMin, timeMax, waitMin, waitMax);
for (Node c = bait.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("catch".equalsIgnoreCase(c.getNodeName()))
{
baitData.addReward(parseInteger(c.getAttributes(), "itemId"));
}
}
_baitData.put(baitData.getItemId(), baitData);
}
}
break;
}
}
}
}
}
}
/**
* Gets the fishing rod.
* @param baitItemId the item id
* @return A list of reward item ids
*/
public FishingBaitData getBaitData(int baitItemId)
{
return _baitData.get(baitItemId);
}
public int getBaitDistanceMin()
{
return _baitDistanceMin;
}
public int getBaitDistanceMax()
{
return _baitDistanceMax;
}
public double getExpRateMin()
{
return _expRateMin;
}
public double getExpRateMax()
{
return _expRateMax;
}
public double getSpRateMin()
{
return _spRateMin;
}
public double getSpRateMax()
{
return _spRateMax;
}
/**
* Gets the single instance of FishingData.
* @return single instance of FishingData
*/
public static FishingData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final FishingData _instance = new FishingData();
}
}

View File

@@ -0,0 +1,196 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.L2Henna;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* This class holds the henna related information.<br>
* Cost and required amount to add the henna to the player.<br>
* Cost and retrieved amount for removing the henna from the player.<br>
* Allowed classes to wear each henna.
* @author Zoey76, Mobius
*/
public final class HennaData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(HennaData.class.getName());
private final Map<Integer, L2Henna> _hennaList = new HashMap<>();
/**
* Instantiates a new henna data.
*/
protected HennaData()
{
load();
}
@Override
public void load()
{
_hennaList.clear();
parseDatapackFile("data/stats/hennaList.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _hennaList.size() + " Henna data.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("henna".equals(d.getNodeName()))
{
parseHenna(d);
}
}
}
}
}
/**
* Parses the henna.
* @param d the d
*/
private void parseHenna(Node d)
{
final StatsSet set = new StatsSet();
final List<ClassId> wearClassIds = new ArrayList<>();
final List<Skill> skills = new ArrayList<>();
NamedNodeMap attrs = d.getAttributes();
Node attr;
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
final String name = c.getNodeName();
attrs = c.getAttributes();
switch (name)
{
case "stats":
{
for (int i = 0; i < attrs.getLength(); i++)
{
attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
break;
}
case "wear":
{
attr = attrs.getNamedItem("count");
set.set("wear_count", attr.getNodeValue());
attr = attrs.getNamedItem("fee");
set.set("wear_fee", attr.getNodeValue());
break;
}
case "cancel":
{
attr = attrs.getNamedItem("count");
set.set("cancel_count", attr.getNodeValue());
attr = attrs.getNamedItem("fee");
set.set("cancel_fee", attr.getNodeValue());
break;
}
case "duration":
{
attr = attrs.getNamedItem("time"); // in minutes
set.set("duration", attr.getNodeValue());
break;
}
case "skill":
{
skills.add(SkillData.getInstance().getSkill(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "classId":
{
wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
break;
}
}
}
final L2Henna henna = new L2Henna(set);
henna.setSkills(skills);
henna.setWearClassIds(wearClassIds);
_hennaList.put(henna.getDyeId(), henna);
}
/**
* Gets the henna.
* @param id of the dye.
* @return the dye with that id.
*/
public L2Henna getHenna(int id)
{
return _hennaList.get(id);
}
/**
* Gets the henna list.
* @param classId the player's class Id.
* @return the list with all the allowed dyes.
*/
public List<L2Henna> getHennaList(ClassId classId)
{
final List<L2Henna> list = new ArrayList<>();
for (L2Henna henna : _hennaList.values())
{
if (henna.isAllowedClass(classId))
{
list.add(henna);
}
}
return list;
}
/**
* Gets the single instance of HennaData.
* @return single instance of HennaData
*/
public static HennaData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final HennaData _instance = new HennaData();
}
}

View File

@@ -0,0 +1,174 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.enums.Position;
import com.l2jmobius.gameserver.model.actor.L2Character;
/**
* This class load, holds and calculates the hit condition bonuses.
* @author Nik
*/
public final class HitConditionBonusData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(HitConditionBonusData.class.getName());
private int frontBonus = 0;
private int sideBonus = 0;
private int backBonus = 0;
private int highBonus = 0;
private int lowBonus = 0;
private int darkBonus = 0;
@SuppressWarnings("unused")
private int rainBonus = 0;
/**
* Instantiates a new hit condition bonus.
*/
protected HitConditionBonusData()
{
load();
}
@Override
public void load()
{
parseDatapackFile("data/stats/hitConditionBonus.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded Hit Condition bonuses.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node d = doc.getFirstChild().getFirstChild(); d != null; d = d.getNextSibling())
{
final NamedNodeMap attrs = d.getAttributes();
switch (d.getNodeName())
{
case "front":
{
frontBonus = parseInteger(attrs, "val");
break;
}
case "side":
{
sideBonus = parseInteger(attrs, "val");
break;
}
case "back":
{
backBonus = parseInteger(attrs, "val");
break;
}
case "high":
{
highBonus = parseInteger(attrs, "val");
break;
}
case "low":
{
lowBonus = parseInteger(attrs, "val");
break;
}
case "dark":
{
darkBonus = parseInteger(attrs, "val");
break;
}
case "rain":
{
rainBonus = parseInteger(attrs, "val");
break;
}
}
}
}
/**
* Gets the condition bonus.
* @param attacker the attacking character.
* @param target the attacked character.
* @return the bonus of the attacker against the target.
*/
public double getConditionBonus(L2Character attacker, L2Character target)
{
double mod = 100;
// Get high or low bonus
if ((attacker.getZ() - target.getZ()) > 50)
{
mod += highBonus;
}
else if ((attacker.getZ() - target.getZ()) < -50)
{
mod += lowBonus;
}
// Get weather bonus
if (GameTimeController.getInstance().isNight())
{
mod += darkBonus;
// else if () No rain support yet.
// chance += hitConditionBonus.rainBonus;
}
// Get side bonus
switch (Position.getPosition(attacker, target))
{
case SIDE:
{
mod += sideBonus;
break;
}
case BACK:
{
mod += backBonus;
break;
}
default:
{
mod += frontBonus;
break;
}
}
// If (mod / 100) is less than 0, return 0, because we can't lower more than 100%.
return Math.max(mod / 100, 0);
}
/**
* Gets the single instance of HitConditionBonus.
* @return single instance of HitConditionBonus
*/
public static HitConditionBonusData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final HitConditionBonusData _instance = new HitConditionBonusData();
}
}

View File

@@ -0,0 +1,142 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.PcItemTemplate;
/**
* This class holds the Initial Equipment information.<br>
* What items get each newly created character and if this item is equipped upon creation (<b>Requires the item to be equippable</b>).
* @author Zoey76
*/
public final class InitialEquipmentData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(InitialEquipmentData.class.getName());
private final Map<ClassId, List<PcItemTemplate>> _initialEquipmentList = new HashMap<>();
private static final String NORMAL = "data/stats/initialEquipment.xml";
private static final String EVENT = "data/stats/initialEquipmentEvent.xml";
/**
* Instantiates a new initial equipment data.
*/
protected InitialEquipmentData()
{
load();
}
@Override
public void load()
{
_initialEquipmentList.clear();
parseDatapackFile(Config.INITIAL_EQUIPMENT_EVENT ? EVENT : NORMAL);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _initialEquipmentList.size() + " Initial Equipment data.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("equipment".equalsIgnoreCase(d.getNodeName()))
{
parseEquipment(d);
}
}
}
}
}
/**
* Parses the equipment.
* @param d parse an initial equipment and add it to {@link #_initialEquipmentList}
*/
private void parseEquipment(Node d)
{
NamedNodeMap attrs = d.getAttributes();
final ClassId classId = ClassId.getClassId(Integer.parseInt(attrs.getNamedItem("classId").getNodeValue()));
final List<PcItemTemplate> equipList = new ArrayList<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("item".equalsIgnoreCase(c.getNodeName()))
{
final StatsSet set = new StatsSet();
attrs = c.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node attr = attrs.item(i);
set.set(attr.getNodeName(), attr.getNodeValue());
}
equipList.add(new PcItemTemplate(set));
}
}
_initialEquipmentList.put(classId, equipList);
}
/**
* Gets the equipment list.
* @param cId the class Id for the required initial equipment.
* @return the initial equipment for the given class Id.
*/
public List<PcItemTemplate> getEquipmentList(ClassId cId)
{
return _initialEquipmentList.get(cId);
}
/**
* Gets the equipment list.
* @param cId the class Id for the required initial equipment.
* @return the initial equipment for the given class Id.
*/
public List<PcItemTemplate> getEquipmentList(int cId)
{
return _initialEquipmentList.get(ClassId.getClassId(cId));
}
/**
* Gets the single instance of InitialEquipmentData.
* @return single instance of InitialEquipmentData
*/
public static InitialEquipmentData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final InitialEquipmentData _instance = new InitialEquipmentData();
}
}

View File

@@ -0,0 +1,371 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.MacroType;
import com.l2jmobius.gameserver.enums.ShortcutType;
import com.l2jmobius.gameserver.model.Macro;
import com.l2jmobius.gameserver.model.MacroCmd;
import com.l2jmobius.gameserver.model.Shortcut;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.network.serverpackets.ShortCutRegister;
/**
* This class holds the Initial Shortcuts information.<br>
* What shortcuts get each newly created character.
* @author Zoey76
*/
public final class InitialShortcutData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(InitialShortcutData.class.getName());
private final Map<ClassId, List<Shortcut>> _initialShortcutData = new HashMap<>();
private final List<Shortcut> _initialGlobalShortcutList = new ArrayList<>();
private final Map<Integer, Macro> _macroPresets = new HashMap<>();
/**
* Instantiates a new initial shortcuts data.
*/
protected InitialShortcutData()
{
load();
}
@Override
public void load()
{
_initialShortcutData.clear();
_initialGlobalShortcutList.clear();
parseDatapackFile("data/stats/initialShortcuts.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _initialGlobalShortcutList.size() + " Initial Global Shortcuts data.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _initialShortcutData.size() + " Initial Shortcuts data.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _macroPresets.size() + " Macros presets.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
switch (d.getNodeName())
{
case "shortcuts":
{
parseShortcuts(d);
break;
}
case "macros":
{
parseMacros(d);
break;
}
}
}
}
}
}
/**
* Parses a shortcut.
* @param d the node
*/
private void parseShortcuts(Node d)
{
NamedNodeMap attrs = d.getAttributes();
final Node classIdNode = attrs.getNamedItem("classId");
final List<Shortcut> list = new ArrayList<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("page".equals(c.getNodeName()))
{
attrs = c.getAttributes();
final int pageId = parseInteger(attrs, "pageId");
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("slot".equals(b.getNodeName()))
{
list.add(createShortcut(pageId, b));
}
}
}
}
if (classIdNode != null)
{
_initialShortcutData.put(ClassId.getClassId(Integer.parseInt(classIdNode.getNodeValue())), list);
}
else
{
_initialGlobalShortcutList.addAll(list);
}
}
/**
* Parses a macro.
* @param d the node
*/
private void parseMacros(Node d)
{
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("macro".equals(c.getNodeName()))
{
NamedNodeMap attrs = c.getAttributes();
if (!parseBoolean(attrs, "enabled", true))
{
continue;
}
final int macroId = parseInteger(attrs, "macroId");
final int icon = parseInteger(attrs, "icon");
final String name = parseString(attrs, "name");
final String description = parseString(attrs, "description");
final String acronym = parseString(attrs, "acronym");
final List<MacroCmd> commands = new ArrayList<>(1);
int entry = 0;
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("command".equals(b.getNodeName()))
{
attrs = b.getAttributes();
final MacroType type = parseEnum(attrs, MacroType.class, "type");
int d1 = 0;
int d2 = 0;
final String cmd = b.getTextContent();
switch (type)
{
case SKILL:
{
d1 = parseInteger(attrs, "skillId"); // Skill ID
d2 = parseInteger(attrs, "skillLvl", 0); // Skill level
break;
}
case ACTION:
{
// Not handled by client.
d1 = parseInteger(attrs, "actionId");
break;
}
case TEXT:
{
// Doesn't have numeric parameters.
break;
}
case SHORTCUT:
{
d1 = parseInteger(attrs, "page"); // Page
d2 = parseInteger(attrs, "slot", 0); // Slot
break;
}
case ITEM:
{
// Not handled by client.
d1 = parseInteger(attrs, "itemId");
break;
}
case DELAY:
{
d1 = parseInteger(attrs, "delay"); // Delay in seconds
break;
}
}
commands.add(new MacroCmd(entry++, type, d1, d2, cmd));
}
}
_macroPresets.put(macroId, new Macro(macroId, icon, name, description, acronym, commands));
}
}
}
/**
* Parses a node an create a shortcut from it.
* @param pageId the page ID
* @param b the node to parse
* @return the new shortcut
*/
private Shortcut createShortcut(int pageId, Node b)
{
final NamedNodeMap attrs = b.getAttributes();
final int slotId = parseInteger(attrs, "slotId");
final ShortcutType shortcutType = parseEnum(attrs, ShortcutType.class, "shortcutType");
final int shortcutId = parseInteger(attrs, "shortcutId");
final int shortcutLevel = parseInteger(attrs, "shortcutLevel", 0);
final int characterType = parseInteger(attrs, "characterType", 0);
return new Shortcut(slotId, pageId, shortcutType, shortcutId, shortcutLevel, 0, characterType);
}
/**
* Gets the shortcut list.
* @param cId the class ID for the shortcut list
* @return the shortcut list for the give class ID
*/
public List<Shortcut> getShortcutList(ClassId cId)
{
return _initialShortcutData.get(cId);
}
/**
* Gets the shortcut list.
* @param cId the class ID for the shortcut list
* @return the shortcut list for the give class ID
*/
public List<Shortcut> getShortcutList(int cId)
{
return _initialShortcutData.get(ClassId.getClassId(cId));
}
/**
* Gets the global shortcut list.
* @return the global shortcut list
*/
public List<Shortcut> getGlobalMacroList()
{
return _initialGlobalShortcutList;
}
/**
* Register all the available shortcuts for the given player.
* @param player the player
*/
public void registerAllShortcuts(L2PcInstance player)
{
if (player == null)
{
return;
}
// Register global shortcuts.
for (Shortcut shortcut : _initialGlobalShortcutList)
{
int shortcutId = shortcut.getId();
switch (shortcut.getType())
{
case ITEM:
{
final L2ItemInstance item = player.getInventory().getItemByItemId(shortcutId);
if (item == null)
{
continue;
}
shortcutId = item.getObjectId();
break;
}
case SKILL:
{
if (!player.getSkills().containsKey(shortcutId))
{
continue;
}
break;
}
case MACRO:
{
final Macro macro = _macroPresets.get(shortcutId);
if (macro == null)
{
continue;
}
player.registerMacro(macro);
break;
}
}
// Register shortcut
final Shortcut newShortcut = new Shortcut(shortcut.getSlot(), shortcut.getPage(), shortcut.getType(), shortcutId, shortcut.getLevel(), shortcut.getSubLevel(), shortcut.getCharacterType());
player.sendPacket(new ShortCutRegister(newShortcut));
player.registerShortCut(newShortcut);
}
// Register class specific shortcuts.
if (_initialShortcutData.containsKey(player.getClassId()))
{
for (Shortcut shortcut : _initialShortcutData.get(player.getClassId()))
{
int shortcutId = shortcut.getId();
switch (shortcut.getType())
{
case ITEM:
{
final L2ItemInstance item = player.getInventory().getItemByItemId(shortcutId);
if (item == null)
{
continue;
}
shortcutId = item.getObjectId();
break;
}
case SKILL:
{
if (!player.getSkills().containsKey(shortcut.getId()))
{
continue;
}
break;
}
case MACRO:
{
final Macro macro = _macroPresets.get(shortcutId);
if (macro == null)
{
continue;
}
player.registerMacro(macro);
break;
}
}
// Register shortcut
final Shortcut newShortcut = new Shortcut(shortcut.getSlot(), shortcut.getPage(), shortcut.getType(), shortcutId, shortcut.getLevel(), shortcut.getSubLevel(), shortcut.getCharacterType());
player.sendPacket(new ShortCutRegister(newShortcut));
player.registerShortCut(newShortcut);
}
}
}
/**
* Gets the single instance of InitialEquipmentData.
* @return single instance of InitialEquipmentData
*/
public static InitialShortcutData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final InitialShortcutData _instance = new InitialShortcutData();
}
}

View File

@@ -0,0 +1,243 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.enums.CrystallizationType;
import com.l2jmobius.gameserver.model.holders.CrystallizationDataHolder;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder;
import com.l2jmobius.gameserver.model.items.L2Armor;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.items.L2Weapon;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.items.type.CrystalType;
/**
* @author UnAfraid
*/
public final class ItemCrystallizationData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ItemCrystallizationData.class.getName());
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
protected ItemCrystallizationData()
{
load();
}
@Override
public void load()
{
_crystallizationTemplates.clear();
for (CrystalType crystalType : CrystalType.values())
{
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
}
_items.clear();
parseDatapackFile("data/CrystallizableItems.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
// Generate remaining data.
generateCrystallizationData();
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node o = n.getFirstChild(); o != null; o = o.getNextSibling())
{
if ("templates".equalsIgnoreCase(o.getNodeName()))
{
for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("crystallizable_template".equalsIgnoreCase(d.getNodeName()))
{
final CrystalType crystalType = parseEnum(d.getAttributes(), CrystalType.class, "crystalType");
final CrystallizationType crystallizationType = parseEnum(d.getAttributes(), CrystallizationType.class, "crystallizationType");
final List<ItemChanceHolder> crystallizeRewards = new ArrayList<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("item".equalsIgnoreCase(c.getNodeName()))
{
NamedNodeMap attrs = c.getAttributes();
final int itemId = parseInteger(attrs, "id");
final long itemCount = parseLong(attrs, "count");
final double itemChance = parseDouble(attrs, "chance");
crystallizeRewards.add(new ItemChanceHolder(itemId, itemChance, itemCount));
}
}
_crystallizationTemplates.get(crystalType).put(crystallizationType, crystallizeRewards);
}
}
}
else if ("items".equalsIgnoreCase(o.getNodeName()))
{
for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("crystallizable_item".equalsIgnoreCase(d.getNodeName()))
{
final int id = parseInteger(d.getAttributes(), "id");
final List<ItemChanceHolder> crystallizeRewards = new ArrayList<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("item".equalsIgnoreCase(c.getNodeName()))
{
NamedNodeMap attrs = c.getAttributes();
final int itemId = parseInteger(attrs, "id");
final long itemCount = parseLong(attrs, "count");
final double itemChance = parseDouble(attrs, "chance");
crystallizeRewards.add(new ItemChanceHolder(itemId, itemChance, itemCount));
}
}
_items.put(id, new CrystallizationDataHolder(id, crystallizeRewards));
}
}
}
}
}
}
}
public int getLoadedCrystallizationTemplateCount()
{
return _crystallizationTemplates.size();
}
private List<ItemChanceHolder> calculateCrystallizeRewards(L2Item item, List<ItemChanceHolder> crystallizeRewards)
{
if (crystallizeRewards == null)
{
return null;
}
final List<ItemChanceHolder> rewards = new ArrayList<>();
for (ItemChanceHolder reward : crystallizeRewards)
{
double chance = reward.getChance() * item.getCrystalCount();
long count = reward.getCount();
if (chance > 100.)
{
double countMul = Math.ceil(chance / 100.);
chance /= countMul;
count *= countMul;
}
rewards.add(new ItemChanceHolder(reward.getId(), chance, count));
}
return rewards;
}
private void generateCrystallizationData()
{
final int previousCount = _items.size();
for (L2Item item : ItemTable.getInstance().getAllItems())
{
// Check if the data has not been generated.
if (((item instanceof L2Weapon) || (item instanceof L2Armor)) && item.isCrystallizable() && !_items.containsKey(item.getId()))
{
final List<ItemChanceHolder> holder = _crystallizationTemplates.get(item.getCrystalType()).get((item instanceof L2Weapon) ? CrystallizationType.WEAPON : CrystallizationType.ARMOR);
if (holder != null)
{
_items.put(item.getId(), new CrystallizationDataHolder(item.getId(), calculateCrystallizeRewards(item, holder)));
}
}
}
LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates.");
}
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
{
return _crystallizationTemplates.get(crystalType).get(crystallizationType);
}
/**
* @param itemId
* @return {@code CrystallizationData} for unenchanted items (enchanted items just have different crystal count, but same rewards),<br>
* or {@code null} if there is no such data registered.
*/
public CrystallizationDataHolder getCrystallizationData(int itemId)
{
return _items.get(itemId);
}
/**
* @param item to calculate its worth in crystals.
* @return List of {@code ItemChanceHolder} for the rewards with altered crystal count.
*/
public List<ItemChanceHolder> getCrystallizationRewards(L2ItemInstance item)
{
final List<ItemChanceHolder> result = new ArrayList<>();
final CrystallizationDataHolder data = getCrystallizationData(item.getId());
if (data != null)
{
// If there are no crystals on the template, add such.
if (data.getItems().stream().noneMatch(i -> i.getId() == item.getItem().getCrystalItemId()))
{
result.add(new ItemChanceHolder(item.getItem().getCrystalItemId(), 100, item.getCrystalCount()));
}
result.addAll(data.getItems());
}
else
{
// Add basic crystal reward.
result.add(new ItemChanceHolder(item.getItem().getCrystalItemId(), 100, item.getCrystalCount()));
}
return result;
}
/**
* Gets the single instance of ItemCrystalizationData.
* @return single instance of ItemCrystalizationData
*/
public static ItemCrystallizationData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ItemCrystallizationData _instance = new ItemCrystallizationData();
}
}

View File

@@ -0,0 +1,99 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
/**
* @author UnAfraid
*/
public class KarmaData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(KarmaData.class.getName());
private final Map<Integer, Double> _karmaTable = new HashMap<>();
public KarmaData()
{
load();
}
@Override
public synchronized void load()
{
_karmaTable.clear();
parseDatapackFile("data/stats/chars/pcKarmaIncrease.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _karmaTable.size() + " karma modifiers.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("pcKarmaIncrease".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("increase".equalsIgnoreCase(d.getNodeName()))
{
final NamedNodeMap attrs = d.getAttributes();
final int level = parseInteger(attrs, "lvl");
if (level >= Config.PLAYER_MAXIMUM_LEVEL)
{
break;
}
_karmaTable.put(level, parseDouble(attrs, "val"));
}
}
}
}
}
/**
* @param level
* @return {@code double} modifier used to calculate karma lost upon death.
*/
public double getMultiplier(int level)
{
return _karmaTable.get(level);
}
/**
* Gets the single instance of KarmaData.
* @return single instance of KarmaData
*/
public static KarmaData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final KarmaData _instance = new KarmaData();
}
}

View File

@@ -0,0 +1,115 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder;
import com.l2jmobius.gameserver.model.holders.ItemPointHolder;
import com.l2jmobius.gameserver.model.holders.LuckyGameDataHolder;
/**
* @author Sdw
*/
public class LuckyGameData implements IGameXmlReader
{
private final Map<Integer, LuckyGameDataHolder> _luckyGame = new HashMap<>();
private final AtomicInteger _serverPlay = new AtomicInteger();
protected LuckyGameData()
{
load();
}
@Override
public void load()
{
_luckyGame.clear();
parseDatapackFile("data/LuckyGameData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _luckyGame.size() + " lucky game data.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "luckygame", rewardNode ->
{
final LuckyGameDataHolder holder = new LuckyGameDataHolder(new StatsSet(parseAttributes(rewardNode)));
forEach(rewardNode, "common_reward", commonRewardNode -> forEach(commonRewardNode, "item", itemNode ->
{
final StatsSet stats = new StatsSet(parseAttributes(itemNode));
holder.addCommonReward(new ItemChanceHolder(stats.getInt("id"), stats.getDouble("chance"), stats.getLong("count")));
}));
forEach(rewardNode, "unique_reward", uniqueRewardNode -> forEach(uniqueRewardNode, "item", itemNode ->
{
holder.addUniqueReward(new ItemPointHolder(new StatsSet(parseAttributes(itemNode))));
}));
forEach(rewardNode, "modify_reward", uniqueRewardNode ->
{
holder.setMinModifyRewardGame(parseInteger(uniqueRewardNode.getAttributes(), "min_game"));
holder.setMaxModifyRewardGame(parseInteger(uniqueRewardNode.getAttributes(), "max_game"));
forEach(uniqueRewardNode, "item", itemNode ->
{
final StatsSet stats = new StatsSet(parseAttributes(itemNode));
holder.addModifyReward(new ItemChanceHolder(stats.getInt("id"), stats.getDouble("chance"), stats.getLong("count")));
});
});
_luckyGame.put(parseInteger(rewardNode.getAttributes(), "index"), holder);
}));
}
public int getLuckyGameCount()
{
return _luckyGame.size();
}
public LuckyGameDataHolder getLuckyGameDataByIndex(int index)
{
return _luckyGame.get(index);
}
public int increaseGame()
{
return _serverPlay.incrementAndGet();
}
public int getServerPlay()
{
return _serverPlay.get();
}
public static LuckyGameData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final LuckyGameData _instance = new LuckyGameData();
}
}

View File

@@ -0,0 +1,138 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.Faction;
import com.l2jmobius.gameserver.model.holders.MonsterBookCardHolder;
import com.l2jmobius.gameserver.model.holders.MonsterBookRewardHolder;
/**
* @author Mobius
*/
public class MonsterBookData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(MonsterBookData.class.getName());
private final List<MonsterBookCardHolder> _monsterBook = new ArrayList<>();
protected MonsterBookData()
{
load();
}
@Override
public void load()
{
_monsterBook.clear();
parseDatapackFile("data/MonsterBook.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _monsterBook.size() + " monster data.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("card".equalsIgnoreCase(d.getNodeName()))
{
final NamedNodeMap attrs = d.getAttributes();
final int itemId = parseInteger(attrs, "id");
final int monster = parseInteger(attrs, "monster");
final String faction = parseString(attrs, "faction");
final MonsterBookCardHolder card = new MonsterBookCardHolder(itemId, monster, Faction.valueOf(faction));
if (NpcData.getInstance().getTemplate(monster) == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Could not find NPC template with id " + monster + ".");
}
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("rewards".equalsIgnoreCase(b.getNodeName()))
{
final NamedNodeMap rewardAttrs = b.getAttributes();
final int kills = parseInteger(rewardAttrs, "kills");
final Long exp = parseLong(rewardAttrs, "exp");
final int sp = parseInteger(rewardAttrs, "sp");
final int points = parseInteger(rewardAttrs, "points");
card.addReward(new MonsterBookRewardHolder(kills, exp, sp, points));
}
}
_monsterBook.add(card);
}
}
}
}
}
public List<MonsterBookCardHolder> getMonsterBookCards()
{
return _monsterBook;
}
public MonsterBookCardHolder getMonsterBookCardByMonsterId(int monsterId)
{
for (MonsterBookCardHolder card : _monsterBook)
{
if (card.getMonsterId() == monsterId)
{
return card;
}
}
return null;
}
public MonsterBookCardHolder getMonsterBookCardById(int cardId)
{
for (MonsterBookCardHolder card : _monsterBook)
{
if (card.getId() == cardId)
{
return card;
}
}
return null;
}
public static MonsterBookData getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final MonsterBookData INSTANCE = new MonsterBookData();
}
}

View File

@@ -0,0 +1,273 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.file.filter.NumericNameFilter;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.enums.SpecialItemType;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.holders.MultisellEntryHolder;
import com.l2jmobius.gameserver.model.holders.MultisellListHolder;
import com.l2jmobius.gameserver.model.holders.PreparedMultisellListHolder;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.network.serverpackets.MultiSellList;
public final class MultisellData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(MultisellData.class.getName());
public static final int PAGE_SIZE = 40;
private static final FileFilter NUMERIC_FILTER = new NumericNameFilter();
private final Map<Integer, MultisellListHolder> _multisells = new HashMap<>();
protected MultisellData()
{
load();
}
@Override
public void load()
{
_multisells.clear();
parseDatapackDirectory("data/multisell", false);
if (Config.CUSTOM_MULTISELL_LOAD)
{
parseDatapackDirectory("data/multisell/custom", false);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _multisells.size() + " multisell lists.");
}
@Override
public void parseDocument(Document doc, File f)
{
try
{
forEach(doc, "list", listNode ->
{
final StatsSet set = new StatsSet(parseAttributes(listNode));
final int listId = Integer.parseInt(f.getName().substring(0, f.getName().length() - 4));
final List<MultisellEntryHolder> entries = new ArrayList<>(listNode.getChildNodes().getLength());
forEach(listNode, itemNode ->
{
if ("item".equalsIgnoreCase(itemNode.getNodeName()))
{
final List<ItemChanceHolder> ingredients = new ArrayList<>(1);
final List<ItemChanceHolder> products = new ArrayList<>(1);
final MultisellEntryHolder entry = new MultisellEntryHolder(ingredients, products);
for (Node d = itemNode.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("ingredient".equalsIgnoreCase(d.getNodeName()))
{
final int id = parseInteger(d.getAttributes(), "id");
final long count = parseLong(d.getAttributes(), "count");
final byte enchantmentLevel = parseByte(d.getAttributes(), "enchantmentLevel", (byte) 0);
final Boolean maintainIngredient = parseBoolean(d.getAttributes(), "maintainIngredient", false);
final ItemChanceHolder ingredient = new ItemChanceHolder(id, 0, count, enchantmentLevel, maintainIngredient);
if (itemExists(ingredient))
{
ingredients.add(ingredient);
}
else
{
LOGGER.warning("Invalid ingredient id or count for itemId: " + ingredient.getId() + ", count: " + ingredient.getCount() + " in list: " + listId);
continue;
}
}
else if ("production".equalsIgnoreCase(d.getNodeName()))
{
final int id = parseInteger(d.getAttributes(), "id");
final long count = parseLong(d.getAttributes(), "count");
final double chance = parseDouble(d.getAttributes(), "chance", Double.NaN);
final byte enchantmentLevel = parseByte(d.getAttributes(), "enchantmentLevel", (byte) 0);
final ItemChanceHolder product = new ItemChanceHolder(id, chance, count, enchantmentLevel);
if (itemExists(product))
{
// Check chance only of items that have set chance. Items without chance (NaN) are used for displaying purposes.
if ((!Double.isNaN(chance) && (chance < 0)) || (chance > 100))
{
LOGGER.warning("Invalid chance for itemId: " + product.getId() + ", count: " + product.getCount() + ", chance: " + chance + " in list: " + listId);
continue;
}
products.add(product);
}
else
{
LOGGER.warning("Invalid product id or count for itemId: " + product.getId() + ", count: " + product.getCount() + " in list: " + listId);
continue;
}
}
}
final double totalChance = products.stream().filter(i -> !Double.isNaN(i.getChance())).mapToDouble(ItemChanceHolder::getChance).sum();
if (totalChance > 100)
{
LOGGER.warning("Products' total chance of " + totalChance + "% exceeds 100% for list: " + listId + " at entry " + entries.size() + 1 + ".");
}
entries.add(entry);
}
else if ("npcs".equalsIgnoreCase(itemNode.getNodeName()))
{
// Initialize NPCs with the size of child nodes.
final Set<Integer> allowNpc = new HashSet<>(itemNode.getChildNodes().getLength());
forEach(itemNode, n -> "npc".equalsIgnoreCase(n.getNodeName()), n -> allowNpc.add(Integer.parseInt(n.getTextContent())));
// Add npcs to stats set.
set.set("allowNpc", allowNpc);
}
});
set.set("listId", listId);
set.set("entries", entries);
_multisells.put(listId, new MultisellListHolder(set));
});
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Error in file " + f, e);
}
}
@Override
public FileFilter getCurrentFileFilter()
{
return NUMERIC_FILTER;
}
/**
* This will generate the multisell list for the items.<br>
* There exist various parameters in multisells that affect the way they will appear:
* <ol>
* <li>Inventory only:
* <ul>
* <li>If true, only show items of the multisell for which the "primary" ingredients are already in the player's inventory. By "primary" ingredients we mean weapon and armor.</li>
* <li>If false, show the entire list.</li>
* </ul>
* </li>
* <li>Maintain enchantment: presumably, only lists with "inventory only" set to true should sometimes have this as true. This makes no sense otherwise...
* <ul>
* <li>If true, then the product will match the enchantment level of the ingredient.<br>
* If the player has multiple items that match the ingredient list but the enchantment levels differ, then the entries need to be duplicated to show the products and ingredients for each enchantment level.<br>
* For example: If the player has a crystal staff +1 and a crystal staff +3 and goes to exchange it at the mammon, the list should have all exchange possibilities for the +1 staff, followed by all possibilities for the +3 staff.</li>
* <li>If false, then any level ingredient will be considered equal and product will always be at +0</li>
* </ul>
* </li>
* <li>Apply taxes: Uses the "taxIngredient" entry in order to add a certain amount of adena to the ingredients.
* <li>
* <li>Additional product and ingredient multipliers.</li>
* </ol>
* @param listId
* @param player
* @param npc
* @param inventoryOnly
* @param ingredientMultiplier
* @param productMultiplier
*/
public final void separateAndSend(int listId, L2PcInstance player, L2Npc npc, boolean inventoryOnly, double ingredientMultiplier, double productMultiplier)
{
final MultisellListHolder template = _multisells.get(listId);
if (template == null)
{
LOGGER.warning("Can't find list id: " + listId + " requested by player: " + player.getName() + ", npcId: " + (npc != null ? npc.getId() : 0));
return;
}
if (!template.isNpcAllowed(-1) && (((npc != null) && !template.isNpcAllowed(npc.getId())) || ((npc == null) && template.isNpcOnly())))
{
if (player.isGM())
{
player.sendMessage("Multisell " + listId + " is restricted. Under current conditions cannot be used. Only GMs are allowed to use it.");
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Player " + player + " attempted to open multisell " + listId + " from npc " + npc + " which is not allowed!");
return;
}
}
// Check if ingredient/product multipliers are set, if not, set them to the template value.
ingredientMultiplier = (Double.isNaN(ingredientMultiplier) ? template.getIngredientMultiplier() : ingredientMultiplier);
productMultiplier = (Double.isNaN(productMultiplier) ? template.getProductMultiplier() : productMultiplier);
final PreparedMultisellListHolder list = new PreparedMultisellListHolder(template, inventoryOnly, player.getInventory(), npc, ingredientMultiplier, productMultiplier);
int index = 0;
do
{
// send list at least once even if size = 0
player.sendPacket(new MultiSellList(list, index));
index += PAGE_SIZE;
}
while (index < list.getEntries().size());
player.setMultiSell(list);
}
public final void separateAndSend(int listId, L2PcInstance player, L2Npc npc, boolean inventoryOnly)
{
separateAndSend(listId, player, npc, inventoryOnly, Double.NaN, Double.NaN);
}
private final boolean itemExists(ItemHolder holder)
{
final SpecialItemType specialItem = SpecialItemType.getByClientId(holder.getId());
if (specialItem != null)
{
return true;
}
final L2Item template = ItemTable.getInstance().getTemplate(holder.getId());
return (template != null) && (template.isStackable() ? (holder.getCount() >= 1) : (holder.getCount() == 1));
}
public static MultisellData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final MultisellData _instance = new MultisellData();
}
}

View File

@@ -0,0 +1,808 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.CommonUtil;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.enums.AISkillScope;
import com.l2jmobius.gameserver.enums.DropType;
import com.l2jmobius.gameserver.enums.MpRewardAffectType;
import com.l2jmobius.gameserver.enums.MpRewardType;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.effects.L2EffectType;
import com.l2jmobius.gameserver.model.holders.DropHolder;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* NPC data parser.
* @author NosBit
*/
public class NpcData implements IGameXmlReader
{
protected static final Logger LOGGER = Logger.getLogger(NpcData.class.getName());
private final Map<Integer, L2NpcTemplate> _npcs = new HashMap<>();
private final Map<String, Integer> _clans = new HashMap<>();
private static final List<Integer> _masterMonsterIDs = new ArrayList<>();
protected NpcData()
{
load();
}
@Override
public synchronized void load()
{
_masterMonsterIDs.clear();
parseDatapackDirectory("data/stats/npcs", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _npcs.size() + " NPCs.");
if (Config.CUSTOM_NPC_DATA)
{
final int npcCount = _npcs.size();
parseDatapackDirectory("data/stats/npcs/custom", true);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + (_npcs.size() - npcCount) + " Custom NPCs.");
}
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node listNode = node.getFirstChild(); listNode != null; listNode = listNode.getNextSibling())
{
if ("npc".equalsIgnoreCase(listNode.getNodeName()))
{
NamedNodeMap attrs = listNode.getAttributes();
final StatsSet set = new StatsSet(new HashMap<>());
final int npcId = parseInteger(attrs, "id");
Map<String, Object> parameters = null;
Map<Integer, Skill> skills = null;
Set<Integer> clans = null;
Set<Integer> ignoreClanNpcIds = null;
List<DropHolder> dropLists = null;
set.set("id", npcId);
set.set("displayId", parseInteger(attrs, "displayId"));
set.set("level", parseByte(attrs, "level"));
set.set("type", parseString(attrs, "type"));
set.set("name", parseString(attrs, "name"));
set.set("usingServerSideName", parseBoolean(attrs, "usingServerSideName"));
set.set("title", parseString(attrs, "title"));
set.set("usingServerSideTitle", parseBoolean(attrs, "usingServerSideTitle"));
for (Node npcNode = listNode.getFirstChild(); npcNode != null; npcNode = npcNode.getNextSibling())
{
attrs = npcNode.getAttributes();
switch (npcNode.getNodeName().toLowerCase())
{
case "parameters":
{
if (parameters == null)
{
parameters = new HashMap<>();
}
parameters.putAll(parseParameters(npcNode));
break;
}
case "race":
case "sex":
{
set.set(npcNode.getNodeName(), npcNode.getTextContent().toUpperCase());
break;
}
case "equipment":
{
set.set("chestId", parseInteger(attrs, "chest"));
set.set("rhandId", parseInteger(attrs, "rhand"));
set.set("lhandId", parseInteger(attrs, "lhand"));
set.set("weaponEnchant", parseInteger(attrs, "weaponEnchant"));
break;
}
case "acquire":
{
set.set("exp", parseDouble(attrs, "exp"));
set.set("sp", parseDouble(attrs, "sp"));
set.set("raidPoints", parseDouble(attrs, "raidPoints"));
break;
}
case "mpreward":
{
set.set("mpRewardValue", parseInteger(attrs, "value"));
set.set("mpRewardType", parseEnum(attrs, MpRewardType.class, "type"));
set.set("mpRewardTicks", parseInteger(attrs, "ticks"));
set.set("mpRewardAffectType", parseEnum(attrs, MpRewardAffectType.class, "affects"));
break;
}
case "stats":
{
set.set("baseSTR", parseInteger(attrs, "str"));
set.set("baseINT", parseInteger(attrs, "int"));
set.set("baseDEX", parseInteger(attrs, "dex"));
set.set("baseWIT", parseInteger(attrs, "wit"));
set.set("baseCON", parseInteger(attrs, "con"));
set.set("baseMEN", parseInteger(attrs, "men"));
for (Node statsNode = npcNode.getFirstChild(); statsNode != null; statsNode = statsNode.getNextSibling())
{
attrs = statsNode.getAttributes();
switch (statsNode.getNodeName().toLowerCase())
{
case "vitals":
{
set.set("baseHpMax", parseDouble(attrs, "hp"));
set.set("baseHpReg", parseDouble(attrs, "hpRegen"));
set.set("baseMpMax", parseDouble(attrs, "mp"));
set.set("baseMpReg", parseDouble(attrs, "mpRegen"));
break;
}
case "attack":
{
set.set("basePAtk", parseDouble(attrs, "physical"));
set.set("baseMAtk", parseDouble(attrs, "magical"));
set.set("baseRndDam", parseInteger(attrs, "random"));
set.set("baseCritRate", parseDouble(attrs, "critical"));
set.set("accuracy", parseFloat(attrs, "accuracy")); // TODO: Implement me
set.set("basePAtkSpd", parseFloat(attrs, "attackSpeed"));
set.set("reuseDelay", parseInteger(attrs, "reuseDelay")); // TODO: Implement me
set.set("baseAtkType", parseString(attrs, "type"));
set.set("baseAtkRange", parseInteger(attrs, "range"));
set.set("distance", parseInteger(attrs, "distance")); // TODO: Implement me
set.set("width", parseInteger(attrs, "width")); // TODO: Implement me
break;
}
case "defence":
{
set.set("basePDef", parseDouble(attrs, "physical"));
set.set("baseMDef", parseDouble(attrs, "magical"));
set.set("evasion", parseInteger(attrs, "evasion")); // TODO: Implement me
set.set("baseShldDef", parseInteger(attrs, "shield"));
set.set("baseShldRate", parseInteger(attrs, "shieldRate"));
break;
}
case "abnormalresist":
{
set.set("physicalAbnormalResist", parseDouble(attrs, "physical"));
set.set("magicAbnormalResist", parseDouble(attrs, "magic"));
break;
}
case "attribute":
{
for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling())
{
attrs = attribute_node.getAttributes();
switch (attribute_node.getNodeName().toLowerCase())
{
case "attack":
{
final String attackAttributeType = parseString(attrs, "type");
switch (attackAttributeType.toUpperCase())
{
case "FIRE":
{
set.set("baseFire", parseInteger(attrs, "value"));
break;
}
case "WATER":
{
set.set("baseWater", parseInteger(attrs, "value"));
break;
}
case "WIND":
{
set.set("baseWind", parseInteger(attrs, "value"));
break;
}
case "EARTH":
{
set.set("baseEarth", parseInteger(attrs, "value"));
break;
}
case "DARK":
{
set.set("baseDark", parseInteger(attrs, "value"));
break;
}
case "HOLY":
{
set.set("baseHoly", parseInteger(attrs, "value"));
break;
}
}
break;
}
case "defence":
{
set.set("baseFireRes", parseInteger(attrs, "fire"));
set.set("baseWaterRes", parseInteger(attrs, "water"));
set.set("baseWindRes", parseInteger(attrs, "wind"));
set.set("baseEarthRes", parseInteger(attrs, "earth"));
set.set("baseHolyRes", parseInteger(attrs, "holy"));
set.set("baseDarkRes", parseInteger(attrs, "dark"));
set.set("baseElementRes", parseInteger(attrs, "default"));
break;
}
}
}
break;
}
case "speed":
{
for (Node speedNode = statsNode.getFirstChild(); speedNode != null; speedNode = speedNode.getNextSibling())
{
attrs = speedNode.getAttributes();
switch (speedNode.getNodeName().toLowerCase())
{
case "walk":
{
set.set("baseWalkSpd", parseDouble(attrs, "ground"));
set.set("baseSwimWalkSpd", parseDouble(attrs, "swim"));
set.set("baseFlyWalkSpd", parseDouble(attrs, "fly"));
break;
}
case "run":
{
set.set("baseRunSpd", parseDouble(attrs, "ground"));
set.set("baseSwimRunSpd", parseDouble(attrs, "swim"));
set.set("baseFlyRunSpd", parseDouble(attrs, "fly"));
break;
}
}
}
break;
}
case "hittime":
{
set.set("hitTime", npcNode.getTextContent()); // TODO: Implement me default 600 (value in ms)
break;
}
}
}
break;
}
case "status":
{
set.set("unique", parseBoolean(attrs, "unique"));
set.set("attackable", parseBoolean(attrs, "attackable"));
set.set("targetable", parseBoolean(attrs, "targetable"));
set.set("talkable", parseBoolean(attrs, "talkable"));
set.set("undying", parseBoolean(attrs, "undying"));
set.set("showName", parseBoolean(attrs, "showName"));
set.set("randomWalk", parseBoolean(attrs, "randomWalk"));
set.set("randomAnimation", parseBoolean(attrs, "randomAnimation"));
set.set("flying", parseBoolean(attrs, "flying"));
set.set("canMove", parseBoolean(attrs, "canMove"));
set.set("noSleepMode", parseBoolean(attrs, "noSleepMode"));
set.set("passableDoor", parseBoolean(attrs, "passableDoor"));
set.set("hasSummoner", parseBoolean(attrs, "hasSummoner"));
set.set("canBeSown", parseBoolean(attrs, "canBeSown"));
set.set("isDeathPenalty", parseBoolean(attrs, "isDeathPenalty"));
set.set("fakePlayer", parseBoolean(attrs, "fakePlayer"));
set.set("fakePlayerTalkable", parseBoolean(attrs, "fakePlayerTalkable"));
break;
}
case "skilllist":
{
skills = new HashMap<>();
for (Node skillListNode = npcNode.getFirstChild(); skillListNode != null; skillListNode = skillListNode.getNextSibling())
{
if ("skill".equalsIgnoreCase(skillListNode.getNodeName()))
{
attrs = skillListNode.getAttributes();
final int skillId = parseInteger(attrs, "id");
final int skillLevel = parseInteger(attrs, "level");
final Skill skill = SkillData.getInstance().getSkill(skillId, skillLevel);
if (skill != null)
{
skills.put(skill.getId(), skill);
}
else
{
LOGGER.warning("[" + f.getName() + "] skill not found. NPC ID: " + npcId + " Skill ID: " + skillId + " Skill Level: " + skillLevel);
}
}
}
break;
}
case "shots":
{
set.set("soulShot", parseInteger(attrs, "soul"));
set.set("spiritShot", parseInteger(attrs, "spirit"));
set.set("shotShotChance", parseInteger(attrs, "shotChance"));
set.set("spiritShotChance", parseInteger(attrs, "spiritChance"));
break;
}
case "corpsetime":
{
set.set("corpseTime", npcNode.getTextContent());
break;
}
case "excrteffect":
{
set.set("exCrtEffect", npcNode.getTextContent()); // TODO: Implement me default ? type boolean
break;
}
case "snpcprophprate":
{
set.set("sNpcPropHpRate", npcNode.getTextContent()); // TODO: Implement me default 1 type double
break;
}
case "ai":
{
set.set("aiType", parseString(attrs, "type"));
set.set("aggroRange", parseInteger(attrs, "aggroRange"));
set.set("clanHelpRange", parseInteger(attrs, "clanHelpRange"));
set.set("dodge", parseInteger(attrs, "dodge"));
set.set("isChaos", parseBoolean(attrs, "isChaos"));
set.set("isAggressive", parseBoolean(attrs, "isAggressive"));
for (Node aiNode = npcNode.getFirstChild(); aiNode != null; aiNode = aiNode.getNextSibling())
{
attrs = aiNode.getAttributes();
switch (aiNode.getNodeName().toLowerCase())
{
case "skill":
{
set.set("minSkillChance", parseInteger(attrs, "minChance"));
set.set("maxSkillChance", parseInteger(attrs, "maxChance"));
set.set("primarySkillId", parseInteger(attrs, "primaryId"));
set.set("shortRangeSkillId", parseInteger(attrs, "shortRangeId"));
set.set("shortRangeSkillChance", parseInteger(attrs, "shortRangeChance"));
set.set("longRangeSkillId", parseInteger(attrs, "longRangeId"));
set.set("longRangeSkillChance", parseInteger(attrs, "longRangeChance"));
break;
}
case "clanlist":
{
for (Node clanListNode = aiNode.getFirstChild(); clanListNode != null; clanListNode = clanListNode.getNextSibling())
{
attrs = clanListNode.getAttributes();
switch (clanListNode.getNodeName().toLowerCase())
{
case "clan":
{
if (clans == null)
{
clans = new HashSet<>(1);
}
clans.add(getOrCreateClanId(clanListNode.getTextContent()));
break;
}
case "ignorenpcid":
{
if (ignoreClanNpcIds == null)
{
ignoreClanNpcIds = new HashSet<>(1);
}
ignoreClanNpcIds.add(Integer.parseInt(clanListNode.getTextContent()));
break;
}
}
}
break;
}
}
}
break;
}
case "droplists":
{
for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling())
{
DropType dropType = null;
try
{
dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase());
}
catch (Exception e)
{
}
if (dropType != null)
{
if (dropLists == null)
{
dropLists = new ArrayList<>();
}
for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling())
{
final NamedNodeMap drop_attrs = drop_node.getAttributes();
if ("item".equals(drop_node.getNodeName().toLowerCase()))
{
final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), parseDouble(drop_attrs, "chance"));
if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null)
{
LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + ".");
}
else
{
dropLists.add(dropItem);
}
}
}
}
}
break;
}
case "extenddrop":
{
final List<Integer> extendDrop = new ArrayList<>();
forEach(npcNode, "id", idNode ->
{
extendDrop.add(Integer.parseInt(idNode.getTextContent()));
});
set.set("extendDrop", extendDrop);
break;
}
case "collision":
{
for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling())
{
attrs = collisionNode.getAttributes();
switch (collisionNode.getNodeName().toLowerCase())
{
case "radius":
{
set.set("collision_radius", parseDouble(attrs, "normal"));
set.set("collisionRadiusGrown", parseDouble(attrs, "grown"));
break;
}
case "height":
{
set.set("collision_height", parseDouble(attrs, "normal"));
set.set("collisionHeightGrown", parseDouble(attrs, "grown"));
break;
}
}
}
break;
}
}
}
L2NpcTemplate template = _npcs.get(npcId);
if (template == null)
{
template = new L2NpcTemplate(set);
_npcs.put(template.getId(), template);
}
else
{
template.set(set);
}
if (parameters != null)
{
// Using unmodifiable map parameters of template are not meant to be changed at runtime.
template.setParameters(new StatsSet(Collections.unmodifiableMap(parameters)));
}
else
{
template.setParameters(StatsSet.EMPTY_STATSET);
}
if (skills != null)
{
Map<AISkillScope, List<Skill>> aiSkillLists = null;
for (Skill skill : skills.values())
{
if (!skill.isPassive())
{
if (aiSkillLists == null)
{
aiSkillLists = new EnumMap<>(AISkillScope.class);
}
final List<AISkillScope> aiSkillScopes = new ArrayList<>();
final AISkillScope shortOrLongRangeScope = skill.getCastRange() <= 150 ? AISkillScope.SHORT_RANGE : AISkillScope.LONG_RANGE;
if (skill.isSuicideAttack())
{
aiSkillScopes.add(AISkillScope.SUICIDE);
}
else
{
aiSkillScopes.add(AISkillScope.GENERAL);
if (skill.isContinuous())
{
if (!skill.isDebuff())
{
aiSkillScopes.add(AISkillScope.BUFF);
}
else
{
aiSkillScopes.add(AISkillScope.DEBUFF);
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
}
else if (skill.hasEffectType(L2EffectType.DISPEL, L2EffectType.DISPEL_BY_SLOT))
{
aiSkillScopes.add(AISkillScope.NEGATIVE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.HEAL))
{
aiSkillScopes.add(AISkillScope.HEAL);
}
else if (skill.hasEffectType(L2EffectType.PHYSICAL_ATTACK, L2EffectType.PHYSICAL_ATTACK_HP_LINK, L2EffectType.MAGICAL_ATTACK, L2EffectType.DEATH_LINK, L2EffectType.HP_DRAIN))
{
aiSkillScopes.add(AISkillScope.ATTACK);
aiSkillScopes.add(AISkillScope.UNIVERSAL);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.SLEEP))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
}
else if (skill.hasEffectType(L2EffectType.BLOCK_ACTIONS, L2EffectType.ROOT))
{
aiSkillScopes.add(AISkillScope.IMMOBILIZE);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.MUTE, L2EffectType.BLOCK_CONTROL))
{
aiSkillScopes.add(AISkillScope.COT);
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.DMG_OVER_TIME, L2EffectType.DMG_OVER_TIME_PERCENT))
{
aiSkillScopes.add(shortOrLongRangeScope);
}
else if (skill.hasEffectType(L2EffectType.RESURRECTION))
{
aiSkillScopes.add(AISkillScope.RES);
}
else
{
aiSkillScopes.add(AISkillScope.UNIVERSAL);
}
}
for (AISkillScope aiSkillScope : aiSkillScopes)
{
List<Skill> aiSkills = aiSkillLists.get(aiSkillScope);
if (aiSkills == null)
{
aiSkills = new ArrayList<>();
aiSkillLists.put(aiSkillScope, aiSkills);
}
aiSkills.add(skill);
}
}
}
template.setSkills(skills);
template.setAISkillLists(aiSkillLists);
}
else
{
template.setSkills(null);
template.setAISkillLists(null);
}
template.setClans(clans);
template.setIgnoreClanNpcIds(ignoreClanNpcIds);
if (dropLists != null)
{
for (DropHolder dropHolder : dropLists)
{
switch (dropHolder.getDropType())
{
case DROP:
case LUCKY: // Lucky drops are added to normal drops and calculated later
{
template.addDrop(dropHolder);
break;
}
case SPOIL:
{
template.addSpoil(dropHolder);
break;
}
}
}
}
if (!template.getParameters().getMinionList("Privates").isEmpty())
{
if (template.getParameters().getSet().get("SummonPrivateRate") == null)
{
_masterMonsterIDs.add(template.getId());
}
}
}
}
}
}
}
/**
* Gets or creates a clan id if it doesnt exists.
* @param clanName the clan name to get or create its id
* @return the clan id for the given clan name
*/
private int getOrCreateClanId(String clanName)
{
Integer id = _clans.get(clanName);
if (id == null)
{
id = _clans.size();
_clans.put(clanName, id);
}
return id;
}
/**
* Gets the clan id
* @param clanName the clan name to get its id
* @return the clan id for the given clan name if it exists, -1 otherwise
*/
public int getClanId(String clanName)
{
final Integer id = _clans.get(clanName);
return id != null ? id : -1;
}
public Set<String> getClansByIds(Set<Integer> clanIds)
{
final Set<String> result = new HashSet<>();
if (clanIds == null)
{
return result;
}
for (Entry<String, Integer> record : _clans.entrySet())
{
for (int id : clanIds)
{
if (record.getValue() == id)
{
result.add(record.getKey());
}
}
}
return result;
}
/**
* Gets the template.
* @param id the template Id to get.
* @return the template for the given id.
*/
public L2NpcTemplate getTemplate(int id)
{
return _npcs.get(id);
}
/**
* Gets the template by name.
* @param name of the template to get.
* @return the template for the given name.
*/
public L2NpcTemplate getTemplateByName(String name)
{
for (L2NpcTemplate npcTemplate : _npcs.values())
{
if (npcTemplate.getName().equalsIgnoreCase(name))
{
return npcTemplate;
}
}
return null;
}
/**
* Gets all templates matching the filter.
* @param filter
* @return the template list for the given filter
*/
public List<L2NpcTemplate> getTemplates(Predicate<L2NpcTemplate> filter)
{
//@formatter:off
return _npcs.values().stream()
.filter(filter)
.collect(Collectors.toList());
//@formatter:on
}
/**
* Gets the all of level.
* @param lvls of all the templates to get.
* @return the template list for the given level.
*/
public List<L2NpcTemplate> getAllOfLevel(int... lvls)
{
return getTemplates(template -> CommonUtil.contains(lvls, template.getLevel()));
}
/**
* Gets the all monsters of level.
* @param lvls of all the monster templates to get.
* @return the template list for the given level.
*/
public List<L2NpcTemplate> getAllMonstersOfLevel(int... lvls)
{
return getTemplates(template -> CommonUtil.contains(lvls, template.getLevel()) && template.isType("L2Monster"));
}
/**
* Gets the all npc starting with.
* @param text of all the NPC templates which its name start with.
* @return the template list for the given letter.
*/
public List<L2NpcTemplate> getAllNpcStartingWith(String text)
{
return getTemplates(template -> template.isType("L2Npc") && template.getName().startsWith(text));
}
/**
* Gets the all npc of class type.
* @param classTypes of all the templates to get.
* @return the template list for the given class type.
*/
public List<L2NpcTemplate> getAllNpcOfClassType(String... classTypes)
{
return getTemplates(template -> CommonUtil.contains(classTypes, template.getType(), true));
}
/**
* @return the IDs of monsters that have minions.
*/
public static List<Integer> getMasterMonsterIDs()
{
return _masterMonsterIDs;
}
/**
* Gets the single instance of NpcData.
* @return single instance of NpcData
*/
public static NpcData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final NpcData _instance = new NpcData();
}
}

View File

@@ -0,0 +1,133 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.handler.EffectHandler;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.options.Options;
import com.l2jmobius.gameserver.model.options.OptionsSkillHolder;
import com.l2jmobius.gameserver.model.options.OptionsSkillType;
/**
* @author UnAfraid
*/
public class OptionData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(OptionData.class.getName());
private final Map<Integer, Options> _optionData = new HashMap<>();
protected OptionData()
{
load();
}
@Override
public synchronized void load()
{
_optionData.clear();
parseDatapackDirectory("data/stats/augmentation/options", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _optionData.size() + " Options.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "option", optionNode ->
{
final int id = parseInteger(optionNode.getAttributes(), "id");
final Options option = new Options(id);
forEach(optionNode, IXmlReader::isNode, innerNode ->
{
switch (innerNode.getNodeName())
{
case "effects":
{
forEach(innerNode, "effect", effectNode ->
{
final String name = parseString(effectNode.getAttributes(), "name");
final StatsSet params = new StatsSet();
forEach(effectNode, IXmlReader::isNode, paramNode ->
{
params.set(paramNode.getNodeName(), SkillData.getInstance().parseValue(paramNode, true, false, Collections.emptyMap()));
});
option.addEffect(EffectHandler.getInstance().getHandlerFactory(name).apply(params));
});
break;
}
case "active_skill":
{
option.addActiveSkill(new SkillHolder(parseInteger(innerNode.getAttributes(), "id"), parseInteger(innerNode.getAttributes(), "level")));
break;
}
case "passive_skill":
{
option.addPassiveSkill(new SkillHolder(parseInteger(innerNode.getAttributes(), "id"), parseInteger(innerNode.getAttributes(), "level")));
break;
}
case "attack_skill":
{
option.addActivationSkill(new OptionsSkillHolder(parseInteger(innerNode.getAttributes(), "id"), parseInteger(innerNode.getAttributes(), "level"), parseDouble(innerNode.getAttributes(), "chance"), OptionsSkillType.ATTACK));
break;
}
case "magic_skill":
{
option.addActivationSkill(new OptionsSkillHolder(parseInteger(innerNode.getAttributes(), "id"), parseInteger(innerNode.getAttributes(), "level"), parseDouble(innerNode.getAttributes(), "chance"), OptionsSkillType.MAGIC));
break;
}
case "critical_skill":
{
option.addActivationSkill(new OptionsSkillHolder(parseInteger(innerNode.getAttributes(), "id"), parseInteger(innerNode.getAttributes(), "level"), parseDouble(innerNode.getAttributes(), "chance"), OptionsSkillType.CRITICAL));
break;
}
}
});
_optionData.put(option.getId(), option);
}));
}
public Options getOptions(int id)
{
return _optionData.get(id);
}
/**
* Gets the single instance of OptionsData.
* @return single instance of OptionsData
*/
public static OptionData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final OptionData _instance = new OptionData();
}
}

View File

@@ -0,0 +1,249 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.MountType;
import com.l2jmobius.gameserver.model.L2PetData;
import com.l2jmobius.gameserver.model.L2PetLevelData;
import com.l2jmobius.gameserver.model.StatsSet;
/**
* This class parse and hold all pet parameters.<br>
* TODO: load and use all pet parameters.
* @author Zoey76 (rework)
*/
public final class PetDataTable implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(PetDataTable.class.getName());
private final Map<Integer, L2PetData> _pets = new HashMap<>();
/**
* Instantiates a new pet data table.
*/
protected PetDataTable()
{
load();
}
@Override
public void load()
{
_pets.clear();
parseDatapackDirectory("data/stats/pets", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _pets.size() + " Pets.");
}
@Override
public void parseDocument(Document doc, File f)
{
NamedNodeMap attrs;
final Node n = doc.getFirstChild();
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if (d.getNodeName().equals("pet"))
{
final int npcId = parseInteger(d.getAttributes(), "id");
final int itemId = parseInteger(d.getAttributes(), "itemId");
// index ignored for now
final L2PetData data = new L2PetData(npcId, itemId);
for (Node p = d.getFirstChild(); p != null; p = p.getNextSibling())
{
if (p.getNodeName().equals("set"))
{
attrs = p.getAttributes();
final String type = attrs.getNamedItem("name").getNodeValue();
if ("food".equals(type))
{
for (String foodId : attrs.getNamedItem("val").getNodeValue().split(";"))
{
data.addFood(Integer.valueOf(foodId));
}
}
else if ("load".equals(type))
{
data.setLoad(parseInteger(attrs, "val"));
}
else if ("hungry_limit".equals(type))
{
data.setHungryLimit(parseInteger(attrs, "val"));
}
else if ("sync_level".equals(type))
{
data.setSyncLevel(parseInteger(attrs, "val") == 1);
}
// evolve ignored
}
else if (p.getNodeName().equals("skills"))
{
for (Node s = p.getFirstChild(); s != null; s = s.getNextSibling())
{
if (s.getNodeName().equals("skill"))
{
attrs = s.getAttributes();
data.addNewSkill(parseInteger(attrs, "skillId"), parseInteger(attrs, "skillLvl"), parseInteger(attrs, "minLvl"));
}
}
}
else if (p.getNodeName().equals("stats"))
{
for (Node s = p.getFirstChild(); s != null; s = s.getNextSibling())
{
if (s.getNodeName().equals("stat"))
{
final int level = Integer.parseInt(s.getAttributes().getNamedItem("level").getNodeValue());
final StatsSet set = new StatsSet();
for (Node bean = s.getFirstChild(); bean != null; bean = bean.getNextSibling())
{
if (bean.getNodeName().equals("set"))
{
attrs = bean.getAttributes();
if (attrs.getNamedItem("name").getNodeValue().equals("speed_on_ride"))
{
set.set("walkSpeedOnRide", attrs.getNamedItem("walk").getNodeValue());
set.set("runSpeedOnRide", attrs.getNamedItem("run").getNodeValue());
set.set("slowSwimSpeedOnRide", attrs.getNamedItem("slowSwim").getNodeValue());
set.set("fastSwimSpeedOnRide", attrs.getNamedItem("fastSwim").getNodeValue());
if (attrs.getNamedItem("slowFly") != null)
{
set.set("slowFlySpeedOnRide", attrs.getNamedItem("slowFly").getNodeValue());
}
if (attrs.getNamedItem("fastFly") != null)
{
set.set("fastFlySpeedOnRide", attrs.getNamedItem("fastFly").getNodeValue());
}
}
else
{
set.set(attrs.getNamedItem("name").getNodeValue(), attrs.getNamedItem("val").getNodeValue());
}
}
}
data.addNewStat(level, new L2PetLevelData(set));
}
}
}
}
_pets.put(npcId, data);
}
}
}
/**
* @param itemId
* @return
*/
public L2PetData getPetDataByItemId(int itemId)
{
for (L2PetData data : _pets.values())
{
if (data.getItemId() == itemId)
{
return data;
}
}
return null;
}
/**
* Gets the pet level data.
* @param petId the pet Id.
* @param petLevel the pet level.
* @return the pet's parameters for the given Id and level.
*/
public L2PetLevelData getPetLevelData(int petId, int petLevel)
{
final L2PetData pd = getPetData(petId);
if (pd != null)
{
if (petLevel > pd.getMaxLevel())
{
return pd.getPetLevelData(pd.getMaxLevel());
}
return pd.getPetLevelData(petLevel);
}
return null;
}
/**
* Gets the pet data.
* @param petId the pet Id.
* @return the pet data
*/
public L2PetData getPetData(int petId)
{
if (!_pets.containsKey(petId))
{
LOGGER.info(getClass().getSimpleName() + ": Missing pet data for npcid: " + petId);
}
return _pets.get(petId);
}
/**
* Gets the pet min level.
* @param petId the pet Id.
* @return the pet min level
*/
public int getPetMinLevel(int petId)
{
return _pets.get(petId).getMinLevel();
}
/**
* Gets the pet items by npc.
* @param npcId the NPC ID to get its summoning item
* @return summoning item for the given NPC ID
*/
public int getPetItemsByNpc(int npcId)
{
return _pets.get(npcId).getItemId();
}
/**
* Checks if is mountable.
* @param npcId the NPC Id to verify.
* @return {@code true} if the given Id is from a mountable pet, {@code false} otherwise.
*/
public static boolean isMountable(int npcId)
{
return MountType.findByNpcId(npcId) != MountType.NONE;
}
/**
* Gets the single instance of PetDataTable.
* @return this class unique instance.
*/
public static PetDataTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PetDataTable _instance = new PetDataTable();
}
}

View File

@@ -0,0 +1,173 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
/**
* @author Mobius
*/
public class PetSkillData implements IGameXmlReader
{
private static Logger LOGGER = Logger.getLogger(PetSkillData.class.getName());
private final Map<Integer, Map<Long, SkillHolder>> _skillTrees = new HashMap<>();
protected PetSkillData()
{
load();
}
@Override
public void load()
{
_skillTrees.clear();
parseDatapackFile("data/PetSkillData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _skillTrees.size() + " skills.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("skill".equalsIgnoreCase(d.getNodeName()))
{
final NamedNodeMap attrs = d.getAttributes();
final int npcId = parseInteger(attrs, "npcId");
final int skillId = parseInteger(attrs, "skillId");
final int skillLvl = parseInteger(attrs, "skillLvl");
Map<Long, SkillHolder> skillTree = _skillTrees.get(npcId);
if (skillTree == null)
{
skillTree = new HashMap<>();
_skillTrees.put(npcId, skillTree);
}
if (SkillData.getInstance().getSkill(skillId, skillLvl == 0 ? 1 : skillLvl) != null)
{
skillTree.put(SkillData.getSkillHashCode(skillId, skillLvl + 1), new SkillHolder(skillId, skillLvl));
}
else
{
LOGGER.info(getClass().getSimpleName() + ": Could not find skill with id " + skillId + ", level " + skillLvl + " for NPC " + npcId + ".");
}
}
}
}
}
}
public int getAvailableLevel(L2Summon pet, int skillId)
{
int lvl = 0;
if (!_skillTrees.containsKey(pet.getId()))
{
LOGGER.warning(getClass().getSimpleName() + ": Pet id " + pet.getId() + " does not have any skills assigned.");
return lvl;
}
for (SkillHolder skillHolder : _skillTrees.get(pet.getId()).values())
{
if (skillHolder.getSkillId() != skillId)
{
continue;
}
if (skillHolder.getSkillLevel() == 0)
{
if (pet.getLevel() < 70)
{
lvl = pet.getLevel() / 10;
if (lvl <= 0)
{
lvl = 1;
}
}
else
{
lvl = 7 + ((pet.getLevel() - 70) / 5);
}
// formula usable for skill that have 10 or more skill levels
final int maxLvl = SkillData.getInstance().getMaxLevel(skillHolder.getSkillId());
if (lvl > maxLvl)
{
lvl = maxLvl;
}
break;
}
else if (1 <= pet.getLevel())
{
if (skillHolder.getSkillLevel() > lvl)
{
lvl = skillHolder.getSkillLevel();
}
}
}
return lvl;
}
public List<Integer> getAvailableSkills(L2Summon pet)
{
final List<Integer> skillIds = new ArrayList<>();
if (!_skillTrees.containsKey(pet.getId()))
{
LOGGER.warning(getClass().getSimpleName() + ": Pet id " + pet.getId() + " does not have any skills assigned.");
return skillIds;
}
for (SkillHolder skillHolder : _skillTrees.get(pet.getId()).values())
{
if (skillIds.contains(skillHolder.getSkillId()))
{
continue;
}
skillIds.add(skillHolder.getSkillId());
}
return skillIds;
}
public static PetSkillData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PetSkillData _instance = new PetSkillData();
}
}

View File

@@ -0,0 +1,194 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2PcTemplate;
import com.l2jmobius.gameserver.model.base.ClassId;
/**
* Loads player's base stats.
* @author Forsaiken, Zoey76, GKR
*/
public final class PlayerTemplateData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(PlayerTemplateData.class.getName());
private final Map<ClassId, L2PcTemplate> _playerTemplates = new HashMap<>();
private int _dataCount = 0;
protected PlayerTemplateData()
{
load();
}
@Override
public void load()
{
_playerTemplates.clear();
parseDatapackDirectory("data/stats/chars/baseStats", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _playerTemplates.size() + " character templates.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _dataCount + " level up gain records.");
}
@Override
public void parseDocument(Document doc, File f)
{
NamedNodeMap attrs;
int classId = 0;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("classId".equalsIgnoreCase(d.getNodeName()))
{
classId = Integer.parseInt(d.getTextContent());
}
else if ("staticData".equalsIgnoreCase(d.getNodeName()))
{
final StatsSet set = new StatsSet();
set.set("classId", classId);
final List<Location> creationPoints = new ArrayList<>();
for (Node nd = d.getFirstChild(); nd != null; nd = nd.getNextSibling())
{
// Skip odd nodes
if (nd.getNodeName().equals("#text"))
{
continue;
}
if (nd.getChildNodes().getLength() > 1)
{
for (Node cnd = nd.getFirstChild(); cnd != null; cnd = cnd.getNextSibling())
{
// use L2CharTemplate(superclass) fields for male collision height and collision radius
if (nd.getNodeName().equalsIgnoreCase("collisionMale"))
{
if (cnd.getNodeName().equalsIgnoreCase("radius"))
{
set.set("collision_radius", cnd.getTextContent());
}
else if (cnd.getNodeName().equalsIgnoreCase("height"))
{
set.set("collision_height", cnd.getTextContent());
}
}
if ("node".equalsIgnoreCase(cnd.getNodeName()))
{
attrs = cnd.getAttributes();
creationPoints.add(new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z")));
}
else if ("walk".equalsIgnoreCase(cnd.getNodeName()))
{
set.set("baseWalkSpd", cnd.getTextContent());
}
else if ("run".equalsIgnoreCase(cnd.getNodeName()))
{
set.set("baseRunSpd", cnd.getTextContent());
}
else if ("slowSwim".equals(cnd.getNodeName()))
{
set.set("baseSwimWalkSpd", cnd.getTextContent());
}
else if ("fastSwim".equals(cnd.getNodeName()))
{
set.set("baseSwimRunSpd", cnd.getTextContent());
}
else if (!cnd.getNodeName().equals("#text"))
{
set.set((nd.getNodeName() + cnd.getNodeName()), cnd.getTextContent());
}
}
}
else
{
set.set(nd.getNodeName(), nd.getTextContent());
}
}
// calculate total pdef and mdef from parts
set.set("basePDef", (set.getInt("basePDefchest", 0) + set.getInt("basePDeflegs", 0) + set.getInt("basePDefhead", 0) + set.getInt("basePDeffeet", 0) + set.getInt("basePDefgloves", 0) + set.getInt("basePDefunderwear", 0) + set.getInt("basePDefcloak", 0) + set.getInt("basePDefhair", 0)));
set.set("baseMDef", (set.getInt("baseMDefrear", 0) + set.getInt("baseMDeflear", 0) + set.getInt("baseMDefrfinger", 0) + set.getInt("baseMDefrfinger", 0) + set.getInt("baseMDefneck", 0)));
_playerTemplates.put(ClassId.getClassId(classId), new L2PcTemplate(set, creationPoints));
}
else if ("lvlUpgainData".equalsIgnoreCase(d.getNodeName()))
{
for (Node lvlNode = d.getFirstChild(); lvlNode != null; lvlNode = lvlNode.getNextSibling())
{
if ("level".equalsIgnoreCase(lvlNode.getNodeName()))
{
attrs = lvlNode.getAttributes();
final int level = parseInteger(attrs, "val");
for (Node valNode = lvlNode.getFirstChild(); valNode != null; valNode = valNode.getNextSibling())
{
final String nodeName = valNode.getNodeName();
if ((level < Config.PLAYER_MAXIMUM_LEVEL) && (nodeName.startsWith("hp") || nodeName.startsWith("mp") || nodeName.startsWith("cp")) && _playerTemplates.containsKey(ClassId.getClassId(classId)))
{
_playerTemplates.get(ClassId.getClassId(classId)).setUpgainValue(nodeName, level, Double.parseDouble(valNode.getTextContent()));
_dataCount++;
}
}
}
}
// TODO: Generate stats automatically.
}
}
}
}
}
public L2PcTemplate getTemplate(ClassId classId)
{
return _playerTemplates.get(classId);
}
public L2PcTemplate getTemplate(int classId)
{
return _playerTemplates.get(ClassId.getClassId(classId));
}
public static PlayerTemplateData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PlayerTemplateData _instance = new PlayerTemplateData();
}
}

View File

@@ -0,0 +1,99 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Arrays;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
/**
* This class holds the Player Xp Percent Lost Data for each level for players.
* @author Zealar
*/
public final class PlayerXpPercentLostData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(PlayerXpPercentLostData.class.getName());
private final int _maxlevel = ExperienceData.getInstance().getMaxLevel();
private final double[] _playerXpPercentLost = new double[_maxlevel + 1];
protected PlayerXpPercentLostData()
{
Arrays.fill(_playerXpPercentLost, 1.);
load();
}
@Override
public void load()
{
parseDatapackFile("data/stats/chars/playerXpPercentLost.xml");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("xpLost".equalsIgnoreCase(d.getNodeName()))
{
final NamedNodeMap attrs = d.getAttributes();
final Integer level = parseInteger(attrs, "level");
if (level > _maxlevel)
{
break;
}
_playerXpPercentLost[level] = parseDouble(attrs, "val");
}
}
}
}
}
public double getXpPercent(int level)
{
if (level > _maxlevel)
{
LOGGER.warning("Require to high level inside PlayerXpPercentLostData (" + level + ")");
return _playerXpPercentLost[_maxlevel];
}
return _playerXpPercentLost[level];
}
/**
* Gets the single instance of PlayerXpPercentLostData.
* @return single instance of PlayerXpPercentLostData.
*/
public static PlayerXpPercentLostData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PlayerXpPercentLostData _instance = new PlayerXpPercentLostData();
}
}

View File

@@ -0,0 +1,153 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.datatables.ItemTable;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.primeshop.PrimeShopGroup;
import com.l2jmobius.gameserver.model.primeshop.PrimeShopItem;
import com.l2jmobius.gameserver.network.serverpackets.primeshop.ExBRProductInfo;
/**
* @author Gnacik, UnAfraid
*/
public class PrimeShopData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(PrimeShopData.class.getName());
private final Map<Integer, PrimeShopGroup> _primeItems = new LinkedHashMap<>();
protected PrimeShopData()
{
load();
}
@Override
public void load()
{
_primeItems.clear();
parseDatapackFile("data/PrimeShop.xml");
if (!_primeItems.isEmpty())
{
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _primeItems.size() + " items");
}
else
{
LOGGER.info(getClass().getSimpleName() + ": System is disabled.");
}
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
final NamedNodeMap at = n.getAttributes();
final Node attribute = at.getNamedItem("enabled");
if ((attribute != null) && Boolean.parseBoolean(attribute.getNodeValue()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("item".equalsIgnoreCase(d.getNodeName()))
{
NamedNodeMap attrs = d.getAttributes();
Node att;
final StatsSet set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
final List<PrimeShopItem> items = new ArrayList<>();
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("item".equalsIgnoreCase(b.getNodeName()))
{
attrs = b.getAttributes();
final int itemId = parseInteger(attrs, "itemId");
final int count = parseInteger(attrs, "count");
final L2Item item = ItemTable.getInstance().getTemplate(itemId);
if (item == null)
{
LOGGER.severe(getClass().getSimpleName() + ": Item template null for itemId: " + itemId + " brId: " + set.getInt("id"));
return;
}
items.add(new PrimeShopItem(itemId, count, item.getWeight(), item.isTradeable() ? 1 : 0));
}
}
_primeItems.put(set.getInt("id"), new PrimeShopGroup(set, items));
}
}
}
}
}
}
public void showProductInfo(L2PcInstance player, int brId)
{
final PrimeShopGroup item = _primeItems.get(brId);
if ((player == null) || (item == null))
{
return;
}
player.sendPacket(new ExBRProductInfo(item, player));
}
public PrimeShopGroup getItem(int brId)
{
return _primeItems.get(brId);
}
public Map<Integer, PrimeShopGroup> getPrimeItems()
{
return _primeItems;
}
public static PrimeShopData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final PrimeShopData _instance = new PrimeShopData();
}
}

View File

@@ -0,0 +1,184 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.enums.StatusUpdateType;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.holders.RecipeHolder;
/**
* @author Nik
*/
public class RecipeData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(RecipeData.class.getName());
private final Map<Integer, RecipeHolder> _recipes = new HashMap<>();
protected RecipeData()
{
load();
}
@Override
public void load()
{
_recipes.clear();
parseDatapackFile("data/Recipes.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _recipes.size() + " recipes.");
}
@Override
public void parseDocument(Document doc, File f)
{
StatsSet set;
Node att;
NamedNodeMap attrs;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("recipe".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
final int recipeId = set.getInt("id");
List<ItemHolder> materials = Collections.emptyList();
List<ItemChanceHolder> productGroup = Collections.emptyList();
List<ItemHolder> npcFee = Collections.emptyList();
final Map<StatusUpdateType, Double> statUse = new HashMap<>();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ("materials".equalsIgnoreCase(c.getNodeName()))
{
materials = getItemList(c);
}
else if ("product".equalsIgnoreCase(c.getNodeName()))
{
productGroup = getItemList(c).stream().map(ItemChanceHolder.class::cast).collect(Collectors.toList());
}
else if ("npcFee".equalsIgnoreCase(c.getNodeName()))
{
npcFee = getItemList(c);
}
else if ("statUse".equalsIgnoreCase(c.getNodeName()))
{
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("stat".equalsIgnoreCase(b.getNodeName()))
{
StatusUpdateType stat = StatusUpdateType.valueOf(b.getAttributes().getNamedItem("name").getNodeValue());
double value = Double.parseDouble(b.getAttributes().getNamedItem("val").getNodeValue());
statUse.put(stat, value);
}
}
}
}
_recipes.put(recipeId, new RecipeHolder(set, materials, productGroup, npcFee, statUse));
}
}
}
}
}
private List<ItemHolder> getItemList(Node c)
{
final List<ItemHolder> items = new ArrayList<>();
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("item".equalsIgnoreCase(b.getNodeName()))
{
int itemId = Integer.parseInt(b.getAttributes().getNamedItem("id").getNodeValue());
long itemCount = Long.parseLong(b.getAttributes().getNamedItem("count").getNodeValue());
if (b.getAttributes().getNamedItem("chance") != null)
{
double chance = Double.parseDouble(b.getAttributes().getNamedItem("chance").getNodeValue());
items.add(new ItemChanceHolder(itemId, chance, itemCount));
}
else
{
items.add(new ItemHolder(itemId, itemCount));
}
}
}
return items;
}
/**
* Gets the recipe by recipe item id.
* @param itemId the recipe's item id
* @return {@code RecipeHolder} for the given recipe item id {@code null} if there is no recipe data connected with this recipe item id.
*/
public RecipeHolder getRecipeByRecipeItemId(int itemId)
{
return _recipes.values().stream().filter(r -> r.getItemId() == itemId).findAny().orElse(null);
}
/**
* @param recipeId the id of the recipe, NOT the recipe item id.
* @return {@code RecipeHolder} containing all the info necessary for crafting a recipe or {@code null} if there is no data for this recipeId.
*/
public RecipeHolder getRecipe(int recipeId)
{
return _recipes.get(recipeId);
}
/**
* Gets the single instance of RecipeData.
* @return single instance of RecipeData
*/
public static RecipeData getInstance()
{
return SingletonHolder._instance;
}
/**
* The Class SingletonHolder.
*/
private static class SingletonHolder
{
protected static final RecipeData _instance = new RecipeData();
}
}

View File

@@ -0,0 +1,113 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.residences.ResidenceFunctionTemplate;
/**
* The residence functions data
* @author UnAfraid
*/
public final class ResidenceFunctionsData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ResidenceFunctionsData.class.getName());
private final Map<Integer, List<ResidenceFunctionTemplate>> _functions = new HashMap<>();
protected ResidenceFunctionsData()
{
load();
}
@Override
public synchronized void load()
{
_functions.clear();
parseDatapackFile("data/ResidenceFunctions.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _functions.size() + " functions.");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", list -> forEach(list, "function", func ->
{
final NamedNodeMap attrs = func.getAttributes();
final StatsSet set = new StatsSet(HashMap::new);
for (int i = 0; i < attrs.getLength(); i++)
{
final Node node = attrs.item(i);
set.set(node.getNodeName(), node.getNodeValue());
}
forEach(func, "function", levelNode ->
{
final NamedNodeMap levelAttrs = levelNode.getAttributes();
final StatsSet levelSet = new StatsSet(HashMap::new);
levelSet.merge(set);
for (int i = 0; i < levelAttrs.getLength(); i++)
{
final Node node = levelAttrs.item(i);
levelSet.set(node.getNodeName(), node.getNodeValue());
}
final ResidenceFunctionTemplate template = new ResidenceFunctionTemplate(levelSet);
_functions.computeIfAbsent(template.getId(), key -> new ArrayList<>()).add(template);
});
}));
}
/**
* @param id
* @param level
* @return function template by id and level, null if not available
*/
public ResidenceFunctionTemplate getFunction(int id, int level)
{
return _functions.getOrDefault(id, Collections.emptyList()).stream().filter(template -> template.getLevel() == level).findAny().orElse(null);
}
/**
* @param id
* @return function template by id, null if not available
*/
public List<ResidenceFunctionTemplate> getFunctions(int id)
{
return _functions.get(id);
}
public static ResidenceFunctionsData getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final ResidenceFunctionsData INSTANCE = new ResidenceFunctionsData();
}
}

View File

@@ -0,0 +1,115 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.SayuneEntry;
/**
* @author UnAfraid
*/
public class SayuneData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(SayuneData.class.getName());
private final Map<Integer, SayuneEntry> _maps = new HashMap<>();
protected SayuneData()
{
load();
}
@Override
public void load()
{
parseDatapackFile("data/SayuneData.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _maps.size() + " maps.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("map".equalsIgnoreCase(d.getNodeName()))
{
final int id = parseInteger(d.getAttributes(), "id");
final SayuneEntry map = new SayuneEntry(id);
parseEntries(map, d);
_maps.put(map.getId(), map);
}
}
}
}
}
private final void parseEntries(SayuneEntry lastEntry, Node n)
{
NamedNodeMap attrs;
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("selector".equals(d.getNodeName()) || "choice".equals(d.getNodeName()) || "loc".equals(d.getNodeName()))
{
attrs = d.getAttributes();
final int id = parseInteger(attrs, "id");
final int x = parseInteger(attrs, "x");
final int y = parseInteger(attrs, "y");
final int z = parseInteger(attrs, "z");
parseEntries(lastEntry.addInnerEntry(new SayuneEntry("selector".equals(d.getNodeName()), id, x, y, z)), d);
}
}
}
public SayuneEntry getMap(int id)
{
return _maps.get(id);
}
public Collection<SayuneEntry> getMaps()
{
return _maps.values();
}
/**
* Gets the single instance of SayuneData.
* @return single instance of SayuneData
*/
public static SayuneData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SayuneData _instance = new SayuneData();
}
}

View File

@@ -0,0 +1,142 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
/**
* @author NosBit
*/
public class SecondaryAuthData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(SecondaryAuthData.class.getName());
private final Set<String> _forbiddenPasswords = new HashSet<>();
private boolean _enabled = false;
private int _maxAttempts = 5;
private int _banTime = 480;
private String _recoveryLink = "";
protected SecondaryAuthData()
{
load();
}
@Override
public synchronized void load()
{
_forbiddenPasswords.clear();
parseFile(new File("config/SecondaryAuth.xml"));
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _forbiddenPasswords.size() + " forbidden passwords.");
}
@Override
public void parseDocument(Document doc, File f)
{
try
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node list_node = node.getFirstChild(); list_node != null; list_node = list_node.getNextSibling())
{
if ("enabled".equalsIgnoreCase(list_node.getNodeName()))
{
_enabled = Boolean.parseBoolean(list_node.getTextContent());
}
else if ("maxAttempts".equalsIgnoreCase(list_node.getNodeName()))
{
_maxAttempts = Integer.parseInt(list_node.getTextContent());
}
else if ("banTime".equalsIgnoreCase(list_node.getNodeName()))
{
_banTime = Integer.parseInt(list_node.getTextContent());
}
else if ("recoveryLink".equalsIgnoreCase(list_node.getNodeName()))
{
_recoveryLink = list_node.getTextContent();
}
else if ("forbiddenPasswords".equalsIgnoreCase(list_node.getNodeName()))
{
for (Node forbiddenPasswords_node = list_node.getFirstChild(); forbiddenPasswords_node != null; forbiddenPasswords_node = forbiddenPasswords_node.getNextSibling())
{
if ("password".equalsIgnoreCase(forbiddenPasswords_node.getNodeName()))
{
_forbiddenPasswords.add(forbiddenPasswords_node.getTextContent());
}
}
}
}
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Failed to load secondary auth data from xml.", e);
}
}
public boolean isEnabled()
{
return _enabled;
}
public int getMaxAttempts()
{
return _maxAttempts;
}
public int getBanTime()
{
return _banTime;
}
public String getRecoveryLink()
{
return _recoveryLink;
}
public Set<String> getForbiddenPasswords()
{
return _forbiddenPasswords;
}
public boolean isForbiddenPassword(String password)
{
return _forbiddenPasswords.contains(password);
}
public static SecondaryAuthData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SecondaryAuthData _instance = new SecondaryAuthData();
}
}

View File

@@ -0,0 +1,202 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.VehiclePathPoint;
import com.l2jmobius.gameserver.model.actor.instance.L2ShuttleInstance;
import com.l2jmobius.gameserver.model.actor.templates.L2CharTemplate;
import com.l2jmobius.gameserver.model.shuttle.L2ShuttleData;
import com.l2jmobius.gameserver.model.shuttle.L2ShuttleEngine;
import com.l2jmobius.gameserver.model.shuttle.L2ShuttleStop;
/**
* @author UnAfraid
*/
public final class ShuttleData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(ShuttleData.class.getName());
private final Map<Integer, L2ShuttleData> _shuttles = new HashMap<>();
private final Map<Integer, L2ShuttleInstance> _shuttleInstances = new HashMap<>();
protected ShuttleData()
{
load();
}
@Override
public synchronized void load()
{
if (!_shuttleInstances.isEmpty())
{
for (L2ShuttleInstance shuttle : _shuttleInstances.values())
{
shuttle.deleteMe();
}
_shuttleInstances.clear();
}
parseDatapackFile("data/ShuttleData.xml");
init();
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _shuttles.size() + " Shuttles.");
}
@Override
public void parseDocument(Document doc, File f)
{
NamedNodeMap attrs;
StatsSet set;
Node att;
L2ShuttleData data;
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("shuttle".equalsIgnoreCase(d.getNodeName()))
{
attrs = d.getAttributes();
set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
data = new L2ShuttleData(set);
for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
{
if ("doors".equalsIgnoreCase(b.getNodeName()))
{
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("door".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
data.addDoor(parseInteger(attrs, "id"));
}
}
}
else if ("stops".equalsIgnoreCase(b.getNodeName()))
{
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("stop".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
final L2ShuttleStop stop = new L2ShuttleStop(parseInteger(attrs, "id"));
for (Node z = a.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("dimension".equalsIgnoreCase(z.getNodeName()))
{
attrs = z.getAttributes();
stop.addDimension(new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z")));
}
}
data.addStop(stop);
}
}
}
else if ("routes".equalsIgnoreCase(b.getNodeName()))
{
for (Node a = b.getFirstChild(); a != null; a = a.getNextSibling())
{
if ("route".equalsIgnoreCase(a.getNodeName()))
{
attrs = a.getAttributes();
final List<Location> locs = new ArrayList<>();
for (Node z = a.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("loc".equalsIgnoreCase(z.getNodeName()))
{
attrs = z.getAttributes();
locs.add(new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z")));
}
}
final VehiclePathPoint[] route = new VehiclePathPoint[locs.size()];
int i = 0;
for (Location loc : locs)
{
route[i++] = new VehiclePathPoint(loc);
}
data.addRoute(route);
}
}
}
}
_shuttles.put(data.getId(), data);
}
}
}
}
}
private void init()
{
for (L2ShuttleData data : _shuttles.values())
{
final L2ShuttleInstance shuttle = new L2ShuttleInstance(new L2CharTemplate(new StatsSet()));
shuttle.setData(data);
shuttle.setHeading(data.getLocation().getHeading());
shuttle.setLocationInvisible(data.getLocation());
shuttle.spawnMe();
shuttle.getStat().setMoveSpeed(300);
shuttle.getStat().setRotationSpeed(0);
shuttle.registerEngine(new L2ShuttleEngine(data, shuttle));
shuttle.runEngine(1000);
_shuttleInstances.put(shuttle.getObjectId(), shuttle);
}
}
public L2ShuttleInstance getShuttle(int id)
{
for (L2ShuttleInstance shuttle : _shuttleInstances.values())
{
if ((shuttle.getObjectId() == id) || (shuttle.getId() == id))
{
return shuttle;
}
}
return null;
}
public static ShuttleData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ShuttleData _instance = new ShuttleData();
}
}

View File

@@ -0,0 +1,128 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.SiegeScheduleDate;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.util.Util;
/**
* @author UnAfraid
*/
public class SiegeScheduleData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(SiegeScheduleData.class.getName());
private final List<SiegeScheduleDate> _scheduleData = new ArrayList<>();
protected SiegeScheduleData()
{
load();
}
@Override
public synchronized void load()
{
_scheduleData.clear();
parseDatapackFile("config/SiegeSchedule.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _scheduleData.size() + " siege schedulers.");
if (_scheduleData.isEmpty())
{
_scheduleData.add(new SiegeScheduleDate(new StatsSet()));
LOGGER.info(getClass().getSimpleName() + ": Emergency Loaded: " + _scheduleData.size() + " default siege schedulers.");
}
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node cd = n.getFirstChild(); cd != null; cd = cd.getNextSibling())
{
switch (cd.getNodeName())
{
case "schedule":
{
final StatsSet set = new StatsSet();
final NamedNodeMap attrs = cd.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node node = attrs.item(i);
final String key = node.getNodeName();
String val = node.getNodeValue();
if ("day".equals(key))
{
if (!Util.isDigit(val))
{
val = Integer.toString(getValueForField(val));
}
}
set.set(key, val);
}
_scheduleData.add(new SiegeScheduleDate(set));
break;
}
}
}
}
}
}
private int getValueForField(String field)
{
try
{
return Calendar.class.getField(field).getInt(Calendar.class.getName());
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "", e);
return -1;
}
}
public List<SiegeScheduleDate> getScheduleDates()
{
return _scheduleData;
}
public static SiegeScheduleData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SiegeScheduleData _instance = new SiegeScheduleData();
}
}

View File

@@ -0,0 +1,696 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.handler.EffectHandler;
import com.l2jmobius.gameserver.handler.SkillConditionHandler;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.effects.AbstractEffect;
import com.l2jmobius.gameserver.model.skills.CommonSkill;
import com.l2jmobius.gameserver.model.skills.EffectScope;
import com.l2jmobius.gameserver.model.skills.ISkillCondition;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillConditionScope;
import com.l2jmobius.gameserver.util.exp4j.ExpressionBuilder;
/**
* Skill data parser.
* @author NosBit
*/
public class SkillData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(SkillData.class.getName());
private final Map<Long, Skill> _skills = new HashMap<>();
private final Map<Integer, Integer> _skillsMaxLevel = new HashMap<>();
private class NamedParamInfo
{
private final String _name;
private final Integer _fromLevel;
private final Integer _toLevel;
private final Integer _fromSubLevel;
private final Integer _toSubLevel;
private final Map<Integer, Map<Integer, StatsSet>> _info;
public NamedParamInfo(String name, Integer fromLevel, Integer toLevel, Integer fromSubLevel, Integer toSubLevel, Map<Integer, Map<Integer, StatsSet>> info)
{
_name = name;
_fromLevel = fromLevel;
_toLevel = toLevel;
_fromSubLevel = fromSubLevel;
_toSubLevel = toSubLevel;
_info = info;
}
public String getName()
{
return _name;
}
public Integer getFromLevel()
{
return _fromLevel;
}
public Integer getToLevel()
{
return _toLevel;
}
public Integer getFromSubLevel()
{
return _fromSubLevel;
}
public Integer getToSubLevel()
{
return _toSubLevel;
}
public Map<Integer, Map<Integer, StatsSet>> getInfo()
{
return _info;
}
}
protected SkillData()
{
load();
}
/**
* Provides the skill hash
* @param skill The L2Skill to be hashed
* @return getSkillHashCode(skill.getId(), skill.getLevel())
*/
public static long getSkillHashCode(Skill skill)
{
return getSkillHashCode(skill.getId(), skill.getLevel(), skill.getSubLevel());
}
/**
* Centralized method for easier change of the hashing sys
* @param skillId The Skill Id
* @param skillLevel The Skill Level
* @return The Skill hash number
*/
public static long getSkillHashCode(int skillId, int skillLevel)
{
return getSkillHashCode(skillId, skillLevel, 0);
}
/**
* Centralized method for easier change of the hashing sys
* @param skillId The Skill Id
* @param skillLevel The Skill Level
* @param subSkillLevel The skill sub level
* @return The Skill hash number
*/
public static long getSkillHashCode(int skillId, int skillLevel, int subSkillLevel)
{
return subSkillLevel > 0 ? ((skillId * 4294967296L) + (subSkillLevel * 65536) + skillLevel) : (skillId * 65536) + skillLevel;
}
public Skill getSkill(int skillId, int level)
{
return getSkill(skillId, level, 0);
}
public Skill getSkill(int skillId, int level, int subLevel)
{
final Skill result = _skills.get(getSkillHashCode(skillId, level, subLevel));
if (result != null)
{
return result;
}
// skill/level not found, fix for transformation scripts
final int maxLvl = getMaxLevel(skillId);
// requested level too high
if ((maxLvl > 0) && (level > maxLvl))
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Call to unexisting skill level id: " + skillId + " requested level: " + level + " max level: " + maxLvl + ".", new Throwable());
return _skills.get(getSkillHashCode(skillId, maxLvl));
}
LOGGER.warning(getClass().getSimpleName() + ": No skill info found for skill id " + skillId + " and skill level " + level);
return null;
}
public int getMaxLevel(int skillId)
{
final Integer maxLevel = _skillsMaxLevel.get(skillId);
return maxLevel != null ? maxLevel : 0;
}
/**
* @param addNoble
* @param hasCastle
* @return an array with siege skills. If addNoble == true, will add also Advanced headquarters.
*/
public List<Skill> getSiegeSkills(boolean addNoble, boolean hasCastle)
{
final List<Skill> temp = new LinkedList<>();
temp.add(_skills.get(getSkillHashCode(CommonSkill.IMPRIT_OF_LIGHT.getId(), 1)));
temp.add(_skills.get(getSkillHashCode(CommonSkill.IMPRIT_OF_DARKNESS.getId(), 1)));
temp.add(_skills.get(getSkillHashCode(247, 1))); // Build Headquarters
if (addNoble)
{
temp.add(_skills.get(getSkillHashCode(326, 1))); // Build Advanced Headquarters
}
if (hasCastle)
{
temp.add(_skills.get(getSkillHashCode(844, 1))); // Outpost Construction
temp.add(_skills.get(getSkillHashCode(845, 1))); // Outpost Demolition
}
return temp;
}
@Override
public boolean isValidating()
{
return false;
}
@Override
public synchronized void load()
{
_skills.clear();
_skillsMaxLevel.clear();
parseDatapackDirectory("data/stats/skills/", true);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _skills.size() + " Skills.");
}
public void reload()
{
load();
// Reload Skill Tree as well.
SkillTreesData.getInstance().load();
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node node = doc.getFirstChild(); node != null; node = node.getNextSibling())
{
if ("list".equalsIgnoreCase(node.getNodeName()))
{
for (Node listNode = node.getFirstChild(); listNode != null; listNode = listNode.getNextSibling())
{
if ("skill".equalsIgnoreCase(listNode.getNodeName()))
{
NamedNodeMap attributes = listNode.getAttributes();
final Map<Integer, Set<Integer>> levels = new HashMap<>();
final Map<Integer, Map<Integer, StatsSet>> skillInfo = new HashMap<>();
final StatsSet generalSkillInfo = skillInfo.computeIfAbsent(-1, k -> new HashMap<>()).computeIfAbsent(-1, k -> new StatsSet());
parseAttributes(attributes, "", generalSkillInfo);
final Map<String, Map<Integer, Map<Integer, Object>>> variableValues = new HashMap<>();
final Map<EffectScope, List<NamedParamInfo>> effectParamInfo = new HashMap<>();
final Map<SkillConditionScope, List<NamedParamInfo>> conditionParamInfo = new HashMap<>();
for (Node skillNode = listNode.getFirstChild(); skillNode != null; skillNode = skillNode.getNextSibling())
{
final String skillNodeName = skillNode.getNodeName();
switch (skillNodeName.toLowerCase())
{
case "variable":
{
attributes = skillNode.getAttributes();
final String name = "@" + parseString(attributes, "name");
variableValues.put(name, parseValues(skillNode));
break;
}
case "#text":
{
break;
}
default:
{
final EffectScope effectScope = EffectScope.findByXmlNodeName(skillNodeName);
if (effectScope != null)
{
for (Node effectsNode = skillNode.getFirstChild(); effectsNode != null; effectsNode = effectsNode.getNextSibling())
{
switch (effectsNode.getNodeName().toLowerCase())
{
case "effect":
{
effectParamInfo.computeIfAbsent(effectScope, k -> new LinkedList<>()).add(parseNamedParamInfo(effectsNode, variableValues));
break;
}
}
}
break;
}
final SkillConditionScope skillConditionScope = SkillConditionScope.findByXmlNodeName(skillNodeName);
if (skillConditionScope != null)
{
for (Node conditionNode = skillNode.getFirstChild(); conditionNode != null; conditionNode = conditionNode.getNextSibling())
{
switch (conditionNode.getNodeName().toLowerCase())
{
case "condition":
{
conditionParamInfo.computeIfAbsent(skillConditionScope, k -> new LinkedList<>()).add(parseNamedParamInfo(conditionNode, variableValues));
break;
}
}
}
}
else
{
parseInfo(skillNode, variableValues, skillInfo);
}
break;
}
}
}
final int fromLevel = generalSkillInfo.getInt(".fromLevel", 1);
final int toLevel = generalSkillInfo.getInt(".toLevel", 0);
for (int i = fromLevel; i <= toLevel; i++)
{
levels.computeIfAbsent(i, k -> new HashSet<>()).add(0);
}
skillInfo.forEach((level, subLevelMap) ->
{
if (level == -1)
{
return;
}
subLevelMap.forEach((subLevel, statsSet) ->
{
if (subLevel == -1)
{
return;
}
levels.computeIfAbsent(level, k -> new HashSet<>()).add(subLevel);
});
});
Stream.concat(effectParamInfo.values().stream(), conditionParamInfo.values().stream()).forEach(namedParamInfos ->
{
namedParamInfos.forEach(namedParamInfo ->
{
namedParamInfo.getInfo().forEach((level, subLevelMap) ->
{
if (level == -1)
{
return;
}
subLevelMap.forEach((subLevel, statsSet) ->
{
if (subLevel == -1)
{
return;
}
levels.computeIfAbsent(level, k -> new HashSet<>()).add(subLevel);
});
});
if ((namedParamInfo.getFromLevel() != null) && (namedParamInfo.getToLevel() != null))
{
for (int i = namedParamInfo.getFromLevel(); i <= namedParamInfo.getToLevel(); i++)
{
if ((namedParamInfo.getFromSubLevel() != null) && (namedParamInfo.getToSubLevel() != null))
{
for (int j = namedParamInfo.getFromSubLevel(); j <= namedParamInfo.getToSubLevel(); j++)
{
levels.computeIfAbsent(i, k -> new HashSet<>()).add(j);
}
}
else
{
levels.computeIfAbsent(i, k -> new HashSet<>()).add(0);
}
}
}
});
});
levels.forEach((level, subLevels) ->
{
subLevels.forEach(subLevel ->
{
final StatsSet statsSet = Optional.ofNullable(skillInfo.getOrDefault(level, Collections.emptyMap()).get(subLevel)).orElseGet(StatsSet::new);
skillInfo.getOrDefault(level, Collections.emptyMap()).getOrDefault(-1, StatsSet.EMPTY_STATSET).getSet().forEach(statsSet.getSet()::putIfAbsent);
skillInfo.getOrDefault(-1, Collections.emptyMap()).getOrDefault(-1, StatsSet.EMPTY_STATSET).getSet().forEach(statsSet.getSet()::putIfAbsent);
statsSet.set(".level", level);
statsSet.set(".subLevel", subLevel);
final Skill skill = new Skill(statsSet);
forEachNamedParamInfoParam(effectParamInfo, level, subLevel, ((effectScope, params) ->
{
final String effectName = params.getString(".name");
params.remove(".name");
try
{
final Function<StatsSet, AbstractEffect> effectFunction = EffectHandler.getInstance().getHandlerFactory(effectName);
if (effectFunction != null)
{
skill.addEffect(effectScope, effectFunction.apply(params));
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Missing effect for Skill Id[" + statsSet.getInt(".id") + "] Level[" + level + "] SubLevel[" + subLevel + "] Effect Scope[" + effectScope + "] Effect Name[" + effectName + "]");
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Failed loading effect for Skill Id[" + statsSet.getInt(".id") + "] Level[" + level + "] SubLevel[" + subLevel + "] Effect Scope[" + effectScope + "] Effect Name[" + effectName + "]", e);
}
}));
forEachNamedParamInfoParam(conditionParamInfo, level, subLevel, ((skillConditionScope, params) ->
{
final String conditionName = params.getString(".name");
params.remove(".name");
try
{
final Function<StatsSet, ISkillCondition> conditionFunction = SkillConditionHandler.getInstance().getHandlerFactory(conditionName);
if (conditionFunction != null)
{
skill.addCondition(skillConditionScope, conditionFunction.apply(params));
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Missing condition for Skill Id[" + statsSet.getInt(".id") + "] Level[" + level + "] SubLevel[" + subLevel + "] Effect Scope[" + skillConditionScope + "] Effect Name[" + conditionName + "]");
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Failed loading condition for Skill Id[" + statsSet.getInt(".id") + "] Level[" + level + "] SubLevel[" + subLevel + "] Condition Scope[" + skillConditionScope + "] Condition Name[" + conditionName + "]", e);
}
}));
_skills.put(getSkillHashCode(skill), skill);
_skillsMaxLevel.merge(skill.getId(), skill.getLevel(), Integer::max);
if ((skill.getSubLevel() % 1000) == 1)
{
EnchantSkillGroupsData.getInstance().addRouteForSkill(skill.getId(), skill.getLevel(), skill.getSubLevel());
}
});
});
}
}
}
}
}
private <T> void forEachNamedParamInfoParam(Map<T, List<NamedParamInfo>> paramInfo, int level, int subLevel, BiConsumer<T, StatsSet> consumer)
{
paramInfo.forEach((scope, namedParamInfos) ->
{
namedParamInfos.forEach(namedParamInfo ->
{
if (((namedParamInfo.getFromLevel() == null) && (namedParamInfo.getToLevel() == null)) || ((namedParamInfo.getFromLevel() <= level) && (namedParamInfo.getToLevel() >= level)))
{
if (((namedParamInfo.getFromSubLevel() == null) && (namedParamInfo.getToSubLevel() == null)) || ((namedParamInfo.getFromSubLevel() <= subLevel) && (namedParamInfo.getToSubLevel() >= subLevel)))
{
final StatsSet params = Optional.ofNullable(namedParamInfo.getInfo().getOrDefault(level, Collections.emptyMap()).get(subLevel)).orElseGet(StatsSet::new);
namedParamInfo.getInfo().getOrDefault(level, Collections.emptyMap()).getOrDefault(-1, StatsSet.EMPTY_STATSET).getSet().forEach(params.getSet()::putIfAbsent);
namedParamInfo.getInfo().getOrDefault(-1, Collections.emptyMap()).getOrDefault(-1, StatsSet.EMPTY_STATSET).getSet().forEach(params.getSet()::putIfAbsent);
params.set(".name", namedParamInfo.getName());
consumer.accept(scope, params);
}
}
});
});
}
private NamedParamInfo parseNamedParamInfo(Node node, Map<String, Map<Integer, Map<Integer, Object>>> variableValues)
{
final NamedNodeMap attributes = node.getAttributes();
final String name = parseString(attributes, "name");
final Integer level = parseInteger(attributes, "level");
final Integer fromLevel = parseInteger(attributes, "fromLevel", level);
final Integer toLevel = parseInteger(attributes, "toLevel", level);
final Integer subLevel = parseInteger(attributes, "subLevel");
final Integer fromSubLevel = parseInteger(attributes, "fromSubLevel", subLevel);
final Integer toSubLevel = parseInteger(attributes, "toSubLevel", subLevel);
final Map<Integer, Map<Integer, StatsSet>> info = new HashMap<>();
for (node = node.getFirstChild(); node != null; node = node.getNextSibling())
{
if (!node.getNodeName().equals("#text"))
{
parseInfo(node, variableValues, info);
}
}
return new NamedParamInfo(name, fromLevel, toLevel, fromSubLevel, toSubLevel, info);
}
private void parseInfo(Node node, Map<String, Map<Integer, Map<Integer, Object>>> variableValues, Map<Integer, Map<Integer, StatsSet>> info)
{
Map<Integer, Map<Integer, Object>> values = parseValues(node);
final Object generalValue = values.getOrDefault(-1, Collections.emptyMap()).get(-1);
if (generalValue != null)
{
final String stringGeneralValue = String.valueOf(generalValue);
if (stringGeneralValue.startsWith("@"))
{
final Map<Integer, Map<Integer, Object>> variableValue = variableValues.get(stringGeneralValue);
if (variableValue != null)
{
values = variableValue;
}
else
{
throw new IllegalArgumentException("undefined variable " + stringGeneralValue);
}
}
}
values.forEach((level, subLevelMap) ->
{
subLevelMap.forEach((subLevel, value) ->
{
info.computeIfAbsent(level, k -> new HashMap<>()).computeIfAbsent(subLevel, k -> new StatsSet()).set(node.getNodeName(), value);
});
});
}
private Map<Integer, Map<Integer, Object>> parseValues(Node node)
{
final Map<Integer, Map<Integer, Object>> values = new HashMap<>();
Object parsedValue = parseValue(node, true, false, Collections.emptyMap());
if (parsedValue != null)
{
values.computeIfAbsent(-1, k -> new HashMap<>()).put(-1, parsedValue);
}
else
{
for (node = node.getFirstChild(); node != null; node = node.getNextSibling())
{
if (node.getNodeName().equalsIgnoreCase("value"))
{
final NamedNodeMap attributes = node.getAttributes();
final Integer level = parseInteger(attributes, "level");
if (level != null)
{
parsedValue = parseValue(node, false, false, Collections.emptyMap());
if (parsedValue != null)
{
final Integer subLevel = parseInteger(attributes, "subLevel", -1);
values.computeIfAbsent(level, k -> new HashMap<>()).put(subLevel, parsedValue);
}
}
else
{
final int fromLevel = parseInteger(attributes, "fromLevel");
final int toLevel = parseInteger(attributes, "toLevel");
final int fromSubLevel = parseInteger(attributes, "fromSubLevel", -1);
final int toSubLevel = parseInteger(attributes, "toSubLevel", -1);
for (int i = fromLevel; i <= toLevel; i++)
{
for (int j = fromSubLevel; j <= toSubLevel; j++)
{
final Map<Integer, Object> subValues = values.computeIfAbsent(i, k -> new HashMap<>());
final Map<String, Double> variables = new HashMap<>();
variables.put("index", (i - fromLevel) + 1d);
variables.put("subIndex", (j - fromSubLevel) + 1d);
final Object base = values.getOrDefault(i, Collections.emptyMap()).get(-1);
if ((base != null) && !(base instanceof StatsSet))
{
variables.put("base", Double.parseDouble(String.valueOf(base)));
}
parsedValue = parseValue(node, false, false, variables);
if (parsedValue != null)
{
subValues.put(j, parsedValue);
}
}
}
}
}
}
}
return values;
}
Object parseValue(Node node, boolean blockValue, boolean parseAttributes, Map<String, Double> variables)
{
StatsSet statsSet = null;
List<Object> list = null;
Object text = null;
if (parseAttributes && (!node.getNodeName().equals("value") || !blockValue) && (node.getAttributes().getLength() > 0))
{
statsSet = new StatsSet();
parseAttributes(node.getAttributes(), "", statsSet, variables);
}
for (node = node.getFirstChild(); node != null; node = node.getNextSibling())
{
final String nodeName = node.getNodeName();
switch (node.getNodeName())
{
case "#text":
{
final String value = node.getNodeValue().trim();
if (!value.isEmpty())
{
text = parseNodeValue(value, variables);
}
break;
}
case "item":
{
if (list == null)
{
list = new LinkedList<>();
}
final Object value = parseValue(node, false, true, variables);
if (value != null)
{
list.add(value);
}
break;
}
case "value":
{
if (blockValue)
{
break;
}
}
default:
{
final Object value = parseValue(node, false, true, variables);
if (value != null)
{
if (statsSet == null)
{
statsSet = new StatsSet();
}
statsSet.set(nodeName, value);
}
}
}
}
if (list != null)
{
if (text != null)
{
throw new IllegalArgumentException("Text and list in same node are not allowed. Node[" + node + "]");
}
if (statsSet != null)
{
statsSet.set(".", list);
}
else
{
return list;
}
}
if (text != null)
{
if (list != null)
{
throw new IllegalArgumentException("Text and list in same node are not allowed. Node[" + node + "]");
}
if (statsSet != null)
{
statsSet.set(".", text);
}
else
{
return text;
}
}
return statsSet;
}
private void parseAttributes(NamedNodeMap attributes, String prefix, StatsSet statsSet, Map<String, Double> variables)
{
for (int i = 0; i < attributes.getLength(); i++)
{
final Node attributeNode = attributes.item(i);
statsSet.set(prefix + "." + attributeNode.getNodeName(), parseNodeValue(attributeNode.getNodeValue(), variables));
}
}
private void parseAttributes(NamedNodeMap attributes, String prefix, StatsSet statsSet)
{
parseAttributes(attributes, prefix, statsSet, Collections.emptyMap());
}
private Object parseNodeValue(String value, Map<String, Double> variables)
{
if (value.startsWith("{") && value.endsWith("}"))
{
return new ExpressionBuilder(value).variables(variables.keySet()).build().setVariables(variables).evaluate();
}
return value;
}
public static SkillData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SkillData _instance = new SkillData();
}
}

View File

@@ -0,0 +1,331 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.IXmlReader;
import com.l2jmobius.gameserver.model.ChanceLocation;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.holders.MinionHolder;
import com.l2jmobius.gameserver.model.interfaces.IParameterized;
import com.l2jmobius.gameserver.model.interfaces.ITerritorized;
import com.l2jmobius.gameserver.model.spawns.NpcSpawnTemplate;
import com.l2jmobius.gameserver.model.spawns.SpawnGroup;
import com.l2jmobius.gameserver.model.spawns.SpawnTemplate;
import com.l2jmobius.gameserver.model.zone.form.ZoneNPoly;
import com.l2jmobius.gameserver.model.zone.type.L2BannedSpawnTerritory;
import com.l2jmobius.gameserver.model.zone.type.L2SpawnTerritory;
/**
* @author UnAfraid
*/
public class SpawnsData implements IGameXmlReader
{
protected static final Logger LOGGER = Logger.getLogger(SpawnsData.class.getName());
private final List<SpawnTemplate> _spawns = new LinkedList<>();
protected SpawnsData()
{
load();
}
@Override
public void load()
{
parseDatapackDirectory("data/spawns", true);
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _spawns.stream().flatMap(c -> c.getGroups().stream()).flatMap(c -> c.getSpawns().stream()).count() + " spawns");
}
@Override
public void parseDocument(Document doc, File f)
{
forEach(doc, "list", listNode -> forEach(listNode, "spawn", spawnNode ->
{
try
{
parseSpawn(spawnNode, f, _spawns);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while processing spawn in file: " + f.getAbsolutePath(), e);
}
}));
}
/**
* Initializing all spawns
*/
public void init()
{
if (Config.ALT_DEV_NO_SPAWNS)
{
return;
}
LOGGER.info(getClass().getSimpleName() + ": Initializing spawns...");
_spawns.stream().filter(SpawnTemplate::isSpawningByDefault).forEach(template ->
{
template.spawnAll(null);
template.notifyActivate();
});
LOGGER.info(getClass().getSimpleName() + ": All spawns has been initialized!");
}
/**
* Removing all spawns
*/
public void despawnAll()
{
LOGGER.info(getClass().getSimpleName() + ": Removing all spawns...");
_spawns.forEach(SpawnTemplate::despawnAll);
LOGGER.info(getClass().getSimpleName() + ": All spawns has been removed!");
}
public List<SpawnTemplate> getSpawns()
{
return _spawns;
}
public List<SpawnTemplate> getSpawns(Predicate<SpawnTemplate> condition)
{
return _spawns.stream().filter(condition).collect(Collectors.toList());
}
public List<SpawnGroup> getGroupsByName(String groupName)
{
return _spawns.stream().filter(template -> (template.getName() != null) && groupName.equalsIgnoreCase(template.getName())).flatMap(template -> template.getGroups().stream()).collect(Collectors.toList());
}
public List<NpcSpawnTemplate> getNpcSpawns(Predicate<NpcSpawnTemplate> condition)
{
return _spawns.stream().flatMap(template -> template.getGroups().stream()).flatMap(group -> group.getSpawns().stream()).filter(condition).collect(Collectors.toList());
}
public void parseSpawn(Node spawnsNode, File file, List<SpawnTemplate> spawns)
{
final SpawnTemplate spawnTemplate = new SpawnTemplate(new StatsSet(parseAttributes(spawnsNode)), file);
SpawnGroup defaultGroup = null;
for (Node innerNode = spawnsNode.getFirstChild(); innerNode != null; innerNode = innerNode.getNextSibling())
{
if ("territories".equalsIgnoreCase(innerNode.getNodeName()))
{
parseTerritories(innerNode, spawnTemplate.getFile(), spawnTemplate);
}
else if ("group".equalsIgnoreCase(innerNode.getNodeName()))
{
parseGroup(innerNode, spawnTemplate);
}
else if ("npc".equalsIgnoreCase(innerNode.getNodeName()))
{
if (defaultGroup == null)
{
defaultGroup = new SpawnGroup(StatsSet.EMPTY_STATSET);
}
parseNpc(innerNode, spawnTemplate, defaultGroup);
}
else if ("parameters".equalsIgnoreCase(innerNode.getNodeName()))
{
parseParameters(spawnsNode, spawnTemplate);
}
}
// One static group for all npcs outside group scope
if (defaultGroup != null)
{
spawnTemplate.addGroup(defaultGroup);
}
spawns.add(spawnTemplate);
}
/**
* @param innerNode
* @param file
* @param spawnTemplate
*/
private void parseTerritories(Node innerNode, File file, ITerritorized spawnTemplate)
{
forEach(innerNode, IXmlReader::isNode, territoryNode ->
{
final String name = parseString(territoryNode.getAttributes(), "name", file.getName() + "_" + (spawnTemplate.getTerritories().size() + 1));
final int minZ = parseInteger(territoryNode.getAttributes(), "minZ");
final int maxZ = parseInteger(territoryNode.getAttributes(), "maxZ");
final List<Integer> xNodes = new ArrayList<>();
final List<Integer> yNodes = new ArrayList<>();
forEach(territoryNode, "node", node ->
{
xNodes.add(parseInteger(node.getAttributes(), "x"));
yNodes.add(parseInteger(node.getAttributes(), "y"));
});
final int[] x = xNodes.stream().mapToInt(Integer::valueOf).toArray();
final int[] y = yNodes.stream().mapToInt(Integer::valueOf).toArray();
switch (territoryNode.getNodeName())
{
case "territory":
{
spawnTemplate.addTerritory(new L2SpawnTerritory(name, new ZoneNPoly(x, y, minZ, maxZ)));
break;
}
case "banned_territory":
{
spawnTemplate.addBannedTerritory(new L2BannedSpawnTerritory(name, new ZoneNPoly(x, y, minZ, maxZ)));
break;
}
}
});
}
private void parseGroup(Node n, SpawnTemplate spawnTemplate)
{
final SpawnGroup group = new SpawnGroup(new StatsSet(parseAttributes(n)));
forEach(n, IXmlReader::isNode, npcNode ->
{
switch (npcNode.getNodeName())
{
case "territories":
{
parseTerritories(npcNode, spawnTemplate.getFile(), group);
break;
}
case "npc":
{
parseNpc(npcNode, spawnTemplate, group);
break;
}
}
});
spawnTemplate.addGroup(group);
}
/**
* @param n
* @param spawnTemplate
* @param group
*/
private void parseNpc(Node n, SpawnTemplate spawnTemplate, SpawnGroup group)
{
final NpcSpawnTemplate npcTemplate = new NpcSpawnTemplate(spawnTemplate, group, new StatsSet(parseAttributes(n)));
final L2NpcTemplate template = NpcData.getInstance().getTemplate(npcTemplate.getId());
if (template == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Requested spawn for non existing npc: " + npcTemplate.getId() + " in file: " + spawnTemplate.getFile().getName());
return;
}
if (template.isType("L2Servitor") || template.isType("L2Pet"))
{
LOGGER.warning(getClass().getSimpleName() + ": Requested spawn for " + template.getType() + " " + template.getName() + "(" + template.getId() + ") file: " + spawnTemplate.getFile().getName());
return;
}
if (!Config.FAKE_PLAYERS_ENABLED && template.isFakePlayer())
{
return;
}
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("parameters".equalsIgnoreCase(d.getNodeName()))
{
parseParameters(d, npcTemplate);
}
else if ("minions".equalsIgnoreCase(d.getNodeName()))
{
parseMinions(d, npcTemplate);
}
else if ("locations".equalsIgnoreCase(d.getNodeName()))
{
parseLocations(d, npcTemplate);
}
}
group.addSpawn(npcTemplate);
}
/**
* @param n
* @param npcTemplate
*/
private void parseLocations(Node n, NpcSpawnTemplate npcTemplate)
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("location".equalsIgnoreCase(d.getNodeName()))
{
final int x = parseInteger(d.getAttributes(), "x");
final int y = parseInteger(d.getAttributes(), "y");
final int z = parseInteger(d.getAttributes(), "z");
final int heading = parseInteger(d.getAttributes(), "heading", 0);
final double chance = parseDouble(d.getAttributes(), "chance");
npcTemplate.addSpawnLocation(new ChanceLocation(x, y, z, heading, chance));
}
}
}
/**
* @param n
* @param npcTemplate
*/
private void parseParameters(Node n, IParameterized<StatsSet> npcTemplate)
{
final Map<String, Object> params = parseParameters(n);
npcTemplate.setParameters(!params.isEmpty() ? new StatsSet(Collections.unmodifiableMap(params)) : StatsSet.EMPTY_STATSET);
}
/**
* @param n
* @param npcTemplate
*/
private void parseMinions(Node n, NpcSpawnTemplate npcTemplate)
{
forEach(n, "minion", minionNode ->
{
npcTemplate.addMinion(new MinionHolder(new StatsSet(parseAttributes(minionNode))));
});
}
/**
* Gets the single instance of SpawnsData.
* @return single instance of SpawnsData
*/
public static SpawnsData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SpawnsData _instance = new SpawnsData();
}
}

View File

@@ -0,0 +1,121 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.data.xml.impl;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.instance.L2StaticObjectInstance;
import com.l2jmobius.gameserver.model.actor.templates.L2CharTemplate;
/**
* This class loads and holds all static object data.
* @author UnAfraid
*/
public final class StaticObjectData implements IGameXmlReader
{
private static final Logger LOGGER = Logger.getLogger(StaticObjectData.class.getName());
private final Map<Integer, L2StaticObjectInstance> _staticObjects = new HashMap<>();
/**
* Instantiates a new static objects.
*/
protected StaticObjectData()
{
load();
}
@Override
public void load()
{
_staticObjects.clear();
parseDatapackFile("data/StaticObjects.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _staticObjects.size() + " static object templates.");
}
@Override
public void parseDocument(Document doc, File f)
{
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equalsIgnoreCase(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("object".equalsIgnoreCase(d.getNodeName()))
{
final NamedNodeMap attrs = d.getAttributes();
final StatsSet set = new StatsSet();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node att = attrs.item(i);
set.set(att.getNodeName(), att.getNodeValue());
}
addObject(set);
}
}
}
}
}
/**
* Initialize an static object based on the stats set and add it to the map.
* @param set the stats set to add.
*/
private void addObject(StatsSet set)
{
final L2StaticObjectInstance obj = new L2StaticObjectInstance(new L2CharTemplate(new StatsSet()), set.getInt("id"));
obj.setType(set.getInt("type", 0));
obj.setName(set.getString("name"));
obj.setMap(set.getString("texture", "none"), set.getInt("map_x", 0), set.getInt("map_y", 0));
obj.spawnMe(set.getInt("x"), set.getInt("y"), set.getInt("z"));
_staticObjects.put(obj.getObjectId(), obj);
}
/**
* Gets the static objects.
* @return a collection of static objects.
*/
public Collection<L2StaticObjectInstance> getStaticObjects()
{
return _staticObjects.values();
}
/**
* Gets the single instance of StaticObjects.
* @return single instance of StaticObjects
*/
public static StaticObjectData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final StaticObjectData _instance = new StaticObjectData();
}
}

Some files were not shown because too many files have changed in this diff Show More