diff --git a/game/data/xml/Custom/VipManager.xml b/game/data/xml/Custom/VipManager.xml
new file mode 100644
index 0000000..7d7e373
--- /dev/null
+++ b/game/data/xml/Custom/VipManager.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/game/data/xml/items/custom/VipItens.xml b/game/data/xml/items/custom/VipItens.xml
new file mode 100644
index 0000000..071021b
--- /dev/null
+++ b/game/data/xml/items/custom/VipItens.xml
@@ -0,0 +1,24 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/java/TioPatinhas/Vip/VipDAO.java b/java/TioPatinhas/Vip/VipDAO.java
new file mode 100644
index 0000000..a7e99e4
--- /dev/null
+++ b/java/TioPatinhas/Vip/VipDAO.java
@@ -0,0 +1,72 @@
+package TioPatinhas.Vip;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+
+import net.sf.l2j.L2DatabaseFactory;
+
+/*
+ * @author -= TioPatinhas =-
+ */
+public final class VipDAO
+{
+ private static final String SELECT = "SELECT tier, expire_at FROM character_vip_player WHERE charId=?";
+ private static final String UPSERT = "REPLACE INTO character_vip_player (charId, tier, expire_at) VALUES (?,?,?)";
+ private static final String DELETE = "DELETE FROM character_vip_player WHERE charId=?";
+
+ public static VipStatus load(int charId)
+ {
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement ps = con.prepareStatement(SELECT))
+ {
+ ps.setInt(1, charId);
+ try (ResultSet rs = ps.executeQuery())
+ {
+ if (rs.next())
+ {
+ final String tier = rs.getString("tier");
+ final LocalDateTime exp = rs.getTimestamp("expire_at").toLocalDateTime();
+ return new VipStatus(tier, exp);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static void save(int charId, String tier, LocalDateTime expireAt)
+ {
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement ps = con.prepareStatement(UPSERT))
+ {
+ ps.setInt(1, charId);
+ ps.setString(2, tier);
+ ps.setTimestamp(3, Timestamp.valueOf(expireAt));
+ ps.executeUpdate();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public static void delete(int charId)
+ {
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement ps = con.prepareStatement(DELETE))
+ {
+ ps.setInt(1, charId);
+ ps.executeUpdate();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/java/TioPatinhas/Vip/VipData.java b/java/TioPatinhas/Vip/VipData.java
new file mode 100644
index 0000000..cf6937c
--- /dev/null
+++ b/java/TioPatinhas/Vip/VipData.java
@@ -0,0 +1,326 @@
+package TioPatinhas.Vip;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/*
+ * @author -= TioPatinhas =-
+ */
+public final class VipData
+{
+ private static final Logger _log = Logger.getLogger(VipData.class.getName());
+ private static final VipData INSTANCE = new VipData();
+
+ public static VipData getInstance()
+ {
+ return INSTANCE;
+ }
+
+ private boolean _enabled = false;
+
+ private final Map> _tierChanceAdd = new HashMap<>();
+ private final Map> _tierCountMul = new HashMap<>();
+ private final Map _tierTitleColors = new HashMap<>();
+ private final Map _tierUseTitleColor = new HashMap<>();
+ private final Map _tierXpRate = new HashMap<>();
+ private final Map _tierSpRate = new HashMap<>();
+ private final Map _tierUseXpSp = new HashMap<>();
+
+ private final Map _tierEnchantAddGlobal = new HashMap<>();
+ private final Map _tierEnchantMulGlobal = new HashMap<>();
+
+ private final Map> _tierEnchantAddByScroll = new HashMap<>();
+ private final Map> _tierEnchantMulByScroll = new HashMap<>();
+
+ public static final class TokenEntry
+ {
+ public final String tier;
+ public final int days;
+
+ public TokenEntry(String t, int d)
+ {
+ tier = t;
+ days = d;
+ }
+ }
+
+ private final Map _tokenByItem = new HashMap<>();
+
+ private VipData()
+ {
+ reload();
+ }
+
+ public void reload()
+ {
+ _enabled = false;
+ _tierChanceAdd.clear();
+ _tierCountMul.clear();
+ _tierTitleColors.clear();
+ _tierUseTitleColor.clear();
+ _tierXpRate.clear();
+ _tierSpRate.clear();
+ _tierUseXpSp.clear();
+
+ _tierEnchantAddGlobal.clear();
+ _tierEnchantMulGlobal.clear();
+ _tierEnchantAddByScroll.clear();
+ _tierEnchantMulByScroll.clear();
+
+ _tokenByItem.clear();
+
+ try
+ {
+ final File f = new File("./data/xml/Custom/VipManager.xml");
+ final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setIgnoringComments(true);
+ dbf.setExpandEntityReferences(false);
+ final Document doc = dbf.newDocumentBuilder().parse(f);
+ doc.getDocumentElement().normalize();
+
+ final NamedNodeMap rootAttrs = doc.getDocumentElement().getAttributes();
+ final Node enabledAttr = rootAttrs.getNamedItem("enabled");
+ _enabled = (enabledAttr != null && Boolean.parseBoolean(enabledAttr.getNodeValue()));
+
+ for (Node n = doc.getDocumentElement().getFirstChild(); n != null; n = n.getNextSibling())
+ {
+ if (n.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ if ("Tier".equalsIgnoreCase(n.getNodeName()))
+ {
+ final NamedNodeMap a = n.getAttributes();
+ final String name = a.getNamedItem("name").getNodeValue().toLowerCase();
+
+ // titleColor / useTitleColor
+ Node tc = a.getNamedItem("titleColor");
+ if (tc != null)
+ _tierTitleColors.put(name, tc.getNodeValue());
+
+ Node utc = a.getNamedItem("useTitleColor");
+ final boolean useTitle = (utc != null && Boolean.parseBoolean(utc.getNodeValue()));
+ _tierUseTitleColor.put(name, useTitle);
+
+ // xpRate / spRate / useXpSp
+ double xpRate = 1.0, spRate = 1.0;
+ Node xr = a.getNamedItem("xpRate");
+ Node sr = a.getNamedItem("spRate");
+ if (xr != null)
+ xpRate = Double.parseDouble(xr.getNodeValue());
+ if (sr != null)
+ spRate = Double.parseDouble(sr.getNodeValue());
+ _tierXpRate.put(name, xpRate);
+ _tierSpRate.put(name, spRate);
+
+ Node ux = a.getNamedItem("useXpSp");
+ boolean useXpSp = (ux != null && Boolean.parseBoolean(ux.getNodeValue()));
+ _tierUseXpSp.put(name, useXpSp);
+
+ final Map chanceAdd = new HashMap<>();
+ final Map countMul = new HashMap<>();
+
+ // ENCHANT maps locais do tier
+ final Map enchAddByScroll = new HashMap<>();
+ final Map enchMulByScroll = new HashMap<>();
+ Double enchAddGlobal = null;
+ Double enchMulGlobal = null;
+
+ for (Node c = n.getFirstChild(); c != null; c = c.getNextSibling())
+ {
+ if (c.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ final String nodeName = c.getNodeName();
+
+ // Drops
+ if ("DropBonus".equalsIgnoreCase(nodeName))
+ {
+ final NamedNodeMap ca = c.getAttributes();
+ final int itemId = Integer.parseInt(ca.getNamedItem("itemId").getNodeValue());
+
+ double addPercent = 0.0;
+ Node ap = ca.getNamedItem("addPercent");
+ if (ap != null)
+ addPercent = Double.parseDouble(ap.getNodeValue());
+ if (addPercent != 0.0)
+ chanceAdd.put(itemId, addPercent);
+
+ double mul = 1.0;
+ Node cm = ca.getNamedItem("countMul");
+ if (cm != null)
+ mul = Double.parseDouble(cm.getNodeValue());
+ if (mul != 1.0)
+ countMul.put(itemId, mul);
+
+ continue;
+ }
+
+ // Enchant
+ if ("EnchantBonus".equalsIgnoreCase(nodeName))
+ {
+ final NamedNodeMap ca = c.getAttributes();
+ Node addN = ca.getNamedItem("addPercent");
+ Node mulN = ca.getNamedItem("mulRate");
+ Node sidN = ca.getNamedItem("scrollId");
+
+ final Double add = (addN != null) ? Double.parseDouble(addN.getNodeValue()) : null;
+ final Double mul = (mulN != null) ? Double.parseDouble(mulN.getNodeValue()) : null;
+
+ if (sidN != null)
+ {
+ final int scrollId = Integer.parseInt(sidN.getNodeValue());
+ if (add != null)
+ enchAddByScroll.put(scrollId, add);
+ if (mul != null)
+ enchMulByScroll.put(scrollId, mul);
+ }
+ else
+ {
+ if (add != null)
+ enchAddGlobal = add;
+ if (mul != null)
+ enchMulGlobal = mul;
+ }
+ }
+ }
+
+ _tierChanceAdd.put(name, Collections.unmodifiableMap(chanceAdd));
+ _tierCountMul.put(name, Collections.unmodifiableMap(countMul));
+
+ // salva enchant por-tier
+ _tierEnchantAddByScroll.put(name, Collections.unmodifiableMap(enchAddByScroll));
+ _tierEnchantMulByScroll.put(name, Collections.unmodifiableMap(enchMulByScroll));
+ if (enchAddGlobal != null)
+ _tierEnchantAddGlobal.put(name, enchAddGlobal);
+ if (enchMulGlobal != null)
+ _tierEnchantMulGlobal.put(name, enchMulGlobal);
+
+ continue;
+ }
+
+ if ("Token".equalsIgnoreCase(n.getNodeName()))
+ {
+ final NamedNodeMap a = n.getAttributes();
+ final int itemId = Integer.parseInt(a.getNamedItem("itemId").getNodeValue());
+ final String tier = a.getNamedItem("tier").getNodeValue().toLowerCase();
+ final int days = Integer.parseInt(a.getNamedItem("days").getNodeValue());
+ _tokenByItem.put(itemId, new TokenEntry(tier, days));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ _log.info("VipData: carregado " + _tierChanceAdd.size() + " tiers, " + _tokenByItem.size() + " tokens.");
+ }
+
+ public boolean isEnabled()
+ {
+ return _enabled;
+ }
+
+ public double getAddPercent(String tierName, int itemId)
+ {
+ if (!_enabled || tierName == null)
+ return 0.0;
+ final Map m = _tierChanceAdd.get(tierName.toLowerCase());
+ return (m == null) ? 0.0 : m.getOrDefault(itemId, 0.0);
+ }
+
+ public double getCountMul(String tierName, int itemId)
+ {
+ if (!_enabled || tierName == null)
+ return 1.0;
+ final Map m = _tierCountMul.get(tierName.toLowerCase());
+ return (m == null) ? 1.0 : m.getOrDefault(itemId, 1.0);
+ }
+
+ public String getTitleColor(String tierName)
+ {
+ if (!_enabled || tierName == null)
+ return null;
+ return _tierTitleColors.get(tierName.toLowerCase());
+ }
+
+ public boolean isUseTitleColor(String tierName)
+ {
+ if (!_enabled || tierName == null)
+ return false;
+ return _tierUseTitleColor.getOrDefault(tierName.toLowerCase(), false);
+ }
+
+ public boolean isUseXpSp(String tierName)
+ {
+ if (!_enabled || tierName == null)
+ return false;
+ return _tierUseXpSp.getOrDefault(tierName.toLowerCase(), false);
+ }
+
+ public double getXpRate(String tierName)
+ {
+ if (!_enabled || tierName == null)
+ return 1.0;
+ return _tierXpRate.getOrDefault(tierName.toLowerCase(), 1.0);
+ }
+
+ public double getSpRate(String tierName)
+ {
+ if (!_enabled || tierName == null)
+ return 1.0;
+ return _tierSpRate.getOrDefault(tierName.toLowerCase(), 1.0);
+ }
+
+ public double getEnchantAddPercent(String tierName, int scrollId)
+ {
+ if (!_enabled || tierName == null)
+ return 0.0;
+ final String t = tierName.toLowerCase();
+ final Map byScroll = _tierEnchantAddByScroll.get(t);
+ if (byScroll != null)
+ {
+ final Double v = byScroll.get(scrollId);
+ if (v != null)
+ return v;
+ }
+ return _tierEnchantAddGlobal.getOrDefault(t, 0.0);
+ }
+
+ public double getEnchantMulRate(String tierName, int scrollId)
+ {
+ if (!_enabled || tierName == null)
+ return 1.0;
+ final String t = tierName.toLowerCase();
+ final Map byScroll = _tierEnchantMulByScroll.get(t);
+ if (byScroll != null)
+ {
+ final Double v = byScroll.get(scrollId);
+ if (v != null)
+ return v;
+ }
+ return _tierEnchantMulGlobal.getOrDefault(t, 1.0);
+ }
+
+ public static int parseColor(String hex)
+ {
+ if (hex == null)
+ return 0xFFFFFF;
+ String clean = hex.replace("#", "");
+ return (int) Long.parseLong(clean, 16);
+ }
+
+ public TokenEntry getToken(int itemId)
+ {
+ return _tokenByItem.get(itemId);
+ }
+}
diff --git a/java/TioPatinhas/Vip/VipManager.java b/java/TioPatinhas/Vip/VipManager.java
new file mode 100644
index 0000000..19ac606
--- /dev/null
+++ b/java/TioPatinhas/Vip/VipManager.java
@@ -0,0 +1,205 @@
+package TioPatinhas.Vip;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+import net.sf.l2j.gameserver.network.clientpackets.Say2;
+import net.sf.l2j.gameserver.network.serverpackets.CreatureSay;
+
+/*
+ * @author -= TioPatinhas =-
+ */
+public final class VipManager
+{
+ private static final VipManager INSTANCE = new VipManager();
+
+ public static VipManager getInstance()
+ {
+ return INSTANCE;
+ }
+
+ private VipManager()
+ {
+ }
+
+ public void onLogin(L2PcInstance player)
+ {
+ final VipStatus stage = VipDAO.load(player.getObjectId());
+ if (stage != null && stage.isActive())
+ {
+ player.setNewVipTier(stage.tier);
+ player.setNewVipExpire(stage.expire);
+ applyTitleColorFromTier(player, stage.tier);
+
+ final String left = formatRemaining(LocalDateTime.now(), stage.expire);
+ player.sendPacket(new CreatureSay(0, Say2.CRITICAL_ANNOUNCE, "", "Seu VIP " + stage.tier + " expira em " + left + "."));
+ warnIfLessThanOneDay(player, stage.expire);
+ }
+ else
+ {
+ player.setNewVipTier(null);
+ player.setNewVipExpire(null);
+ if (stage != null)
+ VipDAO.delete(player.getObjectId());
+ resetTitleColorToDefault(player);
+ }
+ }
+
+ public void setVip(L2PcInstance player, String tier, int days)
+ {
+ LocalDateTime base = LocalDateTime.now();
+ if (player.getNewVipExpire() != null && base.isBefore(player.getNewVipExpire()))
+ base = player.getNewVipExpire();
+
+ final LocalDateTime exp = base.plusDays(Math.max(1, days));
+ final String normTier = tier.toLowerCase();
+
+ VipDAO.save(player.getObjectId(), normTier, exp);
+ player.setNewVipTier(normTier);
+ player.setNewVipExpire(exp);
+
+ applyTitleColorFromTier(player, normTier);
+
+ final String left = formatRemaining(LocalDateTime.now(), exp);
+ player.sendMessage("VIP " + normTier + " renovado. Expira em " + left + ".");
+ player.sendPacket(new CreatureSay(0, Say2.CRITICAL_ANNOUNCE, "", "VIP " + normTier + " renovado. Expira em " + left + "."));
+ warnIfLessThanOneDay(player, exp);
+ }
+
+ public void clearVip(L2PcInstance player)
+ {
+ VipDAO.delete(player.getObjectId());
+ player.setNewVipTier(null);
+ player.setNewVipExpire(null);
+ resetTitleColorToDefault(player);
+ player.sendMessage("Seu VIP foi removido.");
+ player.sendPacket(new CreatureSay(0, Say2.CRITICAL_ANNOUNCE, "", "Seu VIP foi removido."));
+ }
+
+ public double getDropAddPercent(L2PcInstance player, int itemId)
+ {
+ if (player == null || player.getNewVipTier() == null)
+ return 0.0;
+ return VipData.getInstance().getAddPercent(player.getNewVipTier(), itemId);
+ }
+
+ public double getCountMultiplier(L2PcInstance player, int itemId)
+ {
+ if (player == null || player.getNewVipTier() == null)
+ return 1.0;
+ return VipData.getInstance().getCountMul(player.getNewVipTier(), itemId);
+ }
+
+ // XP / SP
+ public double getXpRateMultiplier(L2PcInstance player)
+ {
+ if (player == null)
+ return 1.0;
+ final String tier = player.getNewVipTier();
+ if (tier == null)
+ return 1.0;
+ if (!VipData.getInstance().isUseXpSp(tier))
+ return 1.0;
+ return VipData.getInstance().getXpRate(tier);
+ }
+
+ public double getSpRateMultiplier(L2PcInstance player)
+ {
+ if (player == null)
+ return 1.0;
+ final String tier = player.getNewVipTier();
+ if (tier == null)
+ return 1.0;
+ if (!VipData.getInstance().isUseXpSp(tier))
+ return 1.0;
+ return VipData.getInstance().getSpRate(tier);
+ }
+
+ public double getEnchantAddPercent(L2PcInstance player, int scrollId)
+ {
+ if (player == null)
+ return 0.0;
+ final String tier = player.getNewVipTier();
+ if (tier == null)
+ return 0.0;
+ return VipData.getInstance().getEnchantAddPercent(tier, scrollId);
+ }
+
+ public double getEnchantMulRate(L2PcInstance player, int scrollId)
+ {
+ if (player == null)
+ return 1.0;
+ final String tier = player.getNewVipTier();
+ if (tier == null)
+ return 1.0;
+ return VipData.getInstance().getEnchantMulRate(tier, scrollId);
+ }
+
+ private void applyTitleColorFromTier(L2PcInstance player, String tier)
+ {
+ if (player == null || tier == null)
+ return;
+ if (!VipData.getInstance().isUseTitleColor(tier))
+ return;
+
+ final String hex = VipData.getInstance().getTitleColor(tier);
+ if (hex == null)
+ return;
+
+ try
+ {
+ final int rgb = VipData.parseColor(hex); // "#rrggbb" -> 0xRRGGBB
+ player.getAppearance().setTitleColor(rgb);
+ player.broadcastUserInfo();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void resetTitleColorToDefault(L2PcInstance player)
+ {
+ if (player == null)
+ return;
+
+ final int DEFAULT_TITLE_COLOR = 0xFFFF77;
+ try
+ {
+ player.getAppearance().setTitleColor(DEFAULT_TITLE_COLOR);
+ player.broadcastUserInfo();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private static String formatRemaining(LocalDateTime now, LocalDateTime expire)
+ {
+ if (expire == null || !expire.isAfter(now))
+ return "0d 00h 00m";
+
+ Duration day = Duration.between(now, expire);
+ long totalMin = day.toMinutes();
+ long days = totalMin / (24 * 60);
+ long hours = (totalMin % (24 * 60)) / 60;
+ long mins = totalMin % 60;
+ return String.format("%ddias %02dhoras %02dminutos", days, hours, mins);
+ }
+
+ private static void warnIfLessThanOneDay(L2PcInstance player, LocalDateTime expire)
+ {
+ if (expire == null)
+ return;
+
+ Duration days = Duration.between(LocalDateTime.now(), expire);
+ if (!days.isNegative() && days.compareTo(Duration.ofHours(24)) <= 0)
+ {
+ long hours = Math.max(0, days.toHours());
+ player.sendMessage("Aviso: seu VIP expira em menos de 24h (" + hours + "h).");
+ player.sendPacket(new CreatureSay(0, Say2.CRITICAL_ANNOUNCE, "Aviso: ", "seu VIP expira em menos de 24h (" + hours + "h)."));
+ }
+ }
+}
diff --git a/java/TioPatinhas/Vip/VipStatus.java b/java/TioPatinhas/Vip/VipStatus.java
new file mode 100644
index 0000000..53dfd7a
--- /dev/null
+++ b/java/TioPatinhas/Vip/VipStatus.java
@@ -0,0 +1,23 @@
+package TioPatinhas.Vip;
+
+import java.time.LocalDateTime;
+
+/*
+ * @author -= TioPatinhas =-
+ */
+public final class VipStatus
+{
+ public final String tier;
+ public final LocalDateTime expire;
+
+ public VipStatus(String tier, LocalDateTime expire)
+ {
+ this.tier = tier;
+ this.expire = expire;
+ }
+
+ public boolean isActive()
+ {
+ return tier != null && expire != null && LocalDateTime.now().isBefore(expire);
+ }
+}
diff --git a/java/net/sf/l2j/gameserver/GameServer.java b/java/net/sf/l2j/gameserver/GameServer.java
index 88af6d2..41024a0 100644
--- a/java/net/sf/l2j/gameserver/GameServer.java
+++ b/java/net/sf/l2j/gameserver/GameServer.java
@@ -211,6 +211,7 @@
import TioPatinhas.Dungeon.DungeonManager;
import TioPatinhas.Fusion.data.ItemFusionData;
import TioPatinhas.TerritoryWar.TerritoryWarInit;
+import TioPatinhas.Vip.VipData;
import TioPatinhas.WhatsApp.WhatsappBridge;
import hwid.Hwid;
import phantom.FakePlayerConfig;
@@ -576,6 +577,7 @@
_log.info("Battle Farm Event is disabled");
ItemFusionData.getInstance();
+ VipData.getInstance();
Util.printSection("Lotus Dev: Interface");
TeleportLocationDataLotus.getInstance();
diff --git a/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java b/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
index 646ec73..4fc3ad5 100644
--- a/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
+++ b/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
@@ -66,6 +66,7 @@
import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminMonsterRace;
import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminMovieMaker;
import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminMultiTvT;
+import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminNewVip;
import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminOlympiad;
import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminPCBPoint;
import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminPForge;
@@ -184,6 +185,7 @@
registerAdminCommandHandler(new AdminSearch());
registerAdminCommandHandler(new AdminScanPlayers());
registerAdminCommandHandler(new AdminWhatsapp());
+ registerAdminCommandHandler(new AdminNewVip());
}
public void registerAdminCommandHandler(IAdminCommandHandler handler)
diff --git a/java/net/sf/l2j/gameserver/handler/ItemHandler.java b/java/net/sf/l2j/gameserver/handler/ItemHandler.java
index 7ecd306..edef874 100644
--- a/java/net/sf/l2j/gameserver/handler/ItemHandler.java
+++ b/java/net/sf/l2j/gameserver/handler/ItemHandler.java
@@ -50,6 +50,7 @@
import net.sf.l2j.gameserver.handler.itemhandlers.SpecialXMas;
import net.sf.l2j.gameserver.handler.itemhandlers.SpiritShot;
import net.sf.l2j.gameserver.handler.itemhandlers.SummonItems;
+import net.sf.l2j.gameserver.handler.itemhandlers.VipToken;
import net.sf.l2j.gameserver.handler.itemhandlers.clan.ClanLevel;
import net.sf.l2j.gameserver.handler.itemhandlers.clan.ClanReputation;
import net.sf.l2j.gameserver.handler.itemhandlers.clan.skills.SkillAegis;
@@ -144,6 +145,7 @@
registerItemHandler(new RouletteItem());
registerItemHandler(new AgationItems());
registerItemHandler(new CapsuleBox());
+ registerItemHandler(new VipToken());
if (Config.ALLOW_ITEM_RECOVER)
diff --git a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminNewVip.java b/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminNewVip.java
new file mode 100644
index 0000000..dca9a8c
--- /dev/null
+++ b/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminNewVip.java
@@ -0,0 +1,93 @@
+package net.sf.l2j.gameserver.handler.admincommandhandlers;
+
+import java.util.StringTokenizer;
+
+import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
+import net.sf.l2j.gameserver.model.L2World;
+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+
+import TioPatinhas.Vip.VipData;
+import TioPatinhas.Vip.VipManager;
+
+/*
+ * @author -= TioPatinhas =-
+ */
+public class AdminNewVip implements IAdminCommandHandler
+{
+
+ private static final String[] ADMIN_COMMANDS =
+ {
+ "admin_newvip",
+ "admin_clearvip",
+ "admin_reloadvip"
+ };
+
+ private L2PcInstance resolvePlayer(L2PcInstance admin, String nameOrDot)
+ {
+ if (nameOrDot.equals(".") && admin.getTarget() instanceof L2PcInstance)
+ return (L2PcInstance) admin.getTarget();
+ return L2World.getInstance().getPlayer(nameOrDot);
+ }
+
+ @Override
+ public boolean useAdminCommand(String command, L2PcInstance activeChar)
+ {
+ final StringTokenizer st = new StringTokenizer(command, " ");
+ final String cmd = st.nextToken();
+
+ if ("admin_newvip".equalsIgnoreCase(cmd))
+ {
+ if (st.countTokens() < 3)
+ {
+ activeChar.sendMessage("Uso: //newvip ");
+ return true;
+ }
+ final String playerName = st.nextToken();
+ final String tier = st.nextToken().toLowerCase();
+ final int days = Integer.parseInt(st.nextToken());
+
+ final L2PcInstance target = resolvePlayer(activeChar, playerName);
+ if (target == null)
+ {
+ activeChar.sendMessage("Player offline.");
+ return true;
+ }
+
+ VipManager.getInstance().setVip(target, tier, days);
+ activeChar.sendMessage("VIP setado: " + target.getName() + " -> " + tier + " (" + days + "Dia(s))");
+ target.sendMessage("Seu VIP foi ativado: " + tier + " por " + days + " dia(s).");
+ return true;
+ }
+ else if ("admin_clearvip".equalsIgnoreCase(cmd))
+ {
+ if (!st.hasMoreTokens())
+ {
+ activeChar.sendMessage("Uso: //clearvip ");
+ return true;
+ }
+ final L2PcInstance target = resolvePlayer(activeChar, st.nextToken());
+ if (target == null)
+ {
+ activeChar.sendMessage("Player offline.");
+ return true;
+ }
+ VipManager.getInstance().clearVip(target);
+ activeChar.sendMessage("VIP removido de " + target.getName());
+ target.sendMessage("Seu VIP foi removido.");
+ return true;
+ }
+ else if ("admin_reloadvip".equalsIgnoreCase(cmd))
+ {
+ VipData.getInstance().reload();
+ activeChar.sendMessage("O VipData e VipItems Foram recarregado.");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String[] getAdminCommandList()
+ {
+ return ADMIN_COMMANDS;
+ }
+}
diff --git a/java/net/sf/l2j/gameserver/handler/itemhandlers/VipToken.java b/java/net/sf/l2j/gameserver/handler/itemhandlers/VipToken.java
new file mode 100644
index 0000000..b962cad
--- /dev/null
+++ b/java/net/sf/l2j/gameserver/handler/itemhandlers/VipToken.java
@@ -0,0 +1,48 @@
+package net.sf.l2j.gameserver.handler.itemhandlers;
+
+import net.sf.l2j.gameserver.handler.IItemHandler;
+import net.sf.l2j.gameserver.model.actor.L2Playable;
+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+import net.sf.l2j.gameserver.model.item.instance.ItemInstance;
+import net.sf.l2j.gameserver.network.SystemMessageId;
+import net.sf.l2j.gameserver.network.clientpackets.Say2;
+import net.sf.l2j.gameserver.network.serverpackets.CreatureSay;
+import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
+
+import TioPatinhas.Vip.VipData;
+import TioPatinhas.Vip.VipManager;
+
+/*
+ * @author -= TioPatinhas =-
+ */
+public final class VipToken implements IItemHandler
+{
+
+ @Override
+ public void useItem(L2Playable playable, ItemInstance item, boolean forceUse)
+ {
+ if (!(playable instanceof L2PcInstance))
+ return;
+
+ final L2PcInstance player = (L2PcInstance) playable;
+
+ final VipData.TokenEntry token = VipData.getInstance().getToken(item.getItemId());
+ if (token == null)
+ {
+ player.sendMessage("Token VIP inválido ou não configurado no VipManager.xml.");
+ return;
+ }
+
+ if (!player.destroyItemByItemId("VipToken", item.getItemId(), 1, player, true))
+ {
+ player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS));
+ return;
+ }
+
+ final int days = Math.max(1, token.days);
+ VipManager.getInstance().setVip(player, token.tier, days);
+
+ player.sendMessage("VIP " + token.tier + " aplicado/renovado por " + days + " dia(s).");
+ player.sendPacket(new CreatureSay(0, Say2.CRITICAL_ANNOUNCE, "", "VIP " + token.tier + " aplicado/renovado por " + days + " dia(s)."));
+ }
+}
diff --git a/java/net/sf/l2j/gameserver/model/actor/L2Attackable.java b/java/net/sf/l2j/gameserver/model/actor/L2Attackable.java
index 02fc312..ff3a8a9 100644
--- a/java/net/sf/l2j/gameserver/model/actor/L2Attackable.java
+++ b/java/net/sf/l2j/gameserver/model/actor/L2Attackable.java
@@ -76,6 +76,8 @@
import TioPatinhas.BattleFarm.event.AbstractBattleFarm;
import TioPatinhas.BattleFarm.event.RewardMobRegistry;
import TioPatinhas.BossEngine.CustomBossManager;
+import TioPatinhas.Vip.VipData;
+import TioPatinhas.Vip.VipManager;
/**
* This class manages all NPC that can be attacked, such as :
@@ -643,6 +645,16 @@
sp *= Config.VIP_XP_SP_RATE;
}
+ if (attacker != null && attacker.isNewVip() && VipData.getInstance().isUseXpSp(attacker.getNewVipTier()))
+ {
+ final double xpMul = VipManager.getInstance().getXpRateMultiplier(attacker);
+ final double spMul = VipManager.getInstance().getSpRateMultiplier(attacker);
+
+ exp = Math.round(exp * xpMul);
+ sp = (int) Math.round(sp * spMul);
+ }
+
exp *= 1 - penalty;
if (isOverhit() && _overhitAttacker != null && _overhitAttacker.getActingPlayer() != null && attacker == _overhitAttacker.getActingPlayer())
@@ -734,6 +746,17 @@
exp *= Config.VIP_XP_SP_RATE;
sp *= Config.VIP_XP_SP_RATE;
}
+
+ if (attacker != null && attacker.isNewVip() && VipData.getInstance().isUseXpSp(attacker.getNewVipTier()))
+ {
+ final double xpMul = VipManager.getInstance().getXpRateMultiplier(attacker);
+ final double spMul = VipManager.getInstance().getSpRateMultiplier(attacker);
+
+ exp = Math.round(exp * xpMul);
+ sp = (int) Math.round(sp * spMul);
+ }
+
}
if (isChampion())
@@ -751,6 +774,15 @@
exp *= Config.VIP_XP_SP_RATE;
sp *= Config.VIP_XP_SP_RATE;
}
+ if (attacker != null && attacker.isNewVip() && VipData.getInstance().isUseXpSp(attacker.getNewVipTier()))
+ {
+ final double xpMul = VipManager.getInstance().getXpRateMultiplier(attacker);
+ final double spMul = VipManager.getInstance().getSpRateMultiplier(attacker);
+
+ exp = Math.round(exp * xpMul);
+ sp = (int) Math.round(sp * spMul);
+ }
}
exp *= partyMul;
@@ -1069,6 +1101,18 @@
if (isChampion())
dropChance *= Config.CHAMPION_REWARDS;
+ if (lastAttacker != null && VipData.getInstance().isEnabled() && lastAttacker.isNewVip())
+ {
+ final double addPercent = VipManager.getInstance().getDropAddPercent(lastAttacker, drop.getItemId());
+
+ if (addPercent > 0.0)
+ {
+ final double addInChanceUnits = addPercent * (DropData.MAX_CHANCE / 100.0); // MAX_CHANCE = 100%
+ dropChance += addInChanceUnits;
+ }
+ }
+
// Set our limits for chance of drop
if (dropChance < 1)
dropChance = 1;
@@ -1104,6 +1148,27 @@
if (drop.getItemId() >= 6360 && drop.getItemId() <= 6362)
itemCount *= Config.RATE_DROP_SEAL_STONES;
+ if (itemCount > 0 && lastAttacker != null && VipData.getInstance().isEnabled() && lastAttacker.isNewVip())
+ {
+ final double mul = VipManager.getInstance().getCountMultiplier(lastAttacker, drop.getItemId());
+
+ if (mul > 1.0)
+ {
+ final int base = itemCount;
+ final int whole = (int) Math.floor(mul);
+ final double frac = mul - whole;
+
+ long newCount = (long) base * (long) whole;
+ if (frac > 0d && Rnd.nextDouble() < frac)
+ newCount += base;
+
+ if (newCount < 1 && itemCount > 0)
+ newCount = 1;
+ itemCount = (int) Math.min(Integer.MAX_VALUE, newCount);
+ }
+ }
+
if (lastAttacker.isVip())
{
if (drop.getItemId() == Config.VIP_DROP_RATE)
@@ -1187,6 +1252,14 @@
if (isChampion())
dropChance *= Config.CHAMPION_REWARDS;
+ if (lastAttacker != null && VipData.getInstance().isEnabled() && lastAttacker.isNewVip())
+ {
+ final double addPercent = VipManager.getInstance().getDropAddPercent(lastAttacker, drop.getItemId());
+ if (addPercent > 0.0)
+ dropChance += (addPercent * (DropData.MAX_CHANCE / 100.0));
+ }
+
if (dropChance < DropData.MAX_CHANCE)
dropChance = DropData.MAX_CHANCE;
@@ -1232,6 +1305,26 @@
}
}
+ if (itemCount > 0 && lastAttacker != null && VipData.getInstance().isEnabled() && lastAttacker.isNewVip())
+ {
+ final double mul = VipManager.getInstance().getCountMultiplier(lastAttacker, drop.getItemId());
+ if (mul > 1.0)
+ {
+ final int base = itemCount;
+ final int whole = (int) Math.floor(mul);
+ final double frac = mul - whole;
+
+ long newCount = (long) base * (long) whole;
+ if (frac > 0d && Rnd.nextDouble() < frac)
+ newCount += base;
+
+ if (newCount < 1 && itemCount > 0)
+ newCount = 1;
+ itemCount = (int) Math.min(Integer.MAX_VALUE, newCount);
+ }
+ }
+
if (itemCount > 0)
return new ItemHolder(drop.getItemId(), itemCount, drop.getMinEnchant(), drop.getMaxEnchant(), drop.getEnchantSucess());
}
diff --git a/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java b/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java
index ae7ddc0..cf38913 100644
--- a/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java
+++ b/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java
@@ -17,6 +17,7 @@
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -14613,4 +14614,34 @@
{
_originalBatlleTitleColor = color;
}
+
+ // NewVIP
+ private String _vipTier = null;
+ private LocalDateTime _vipExpire = null;
+
+ public void setNewVipTier(String t)
+ {
+ _vipTier = t;
+ }
+
+ public String getNewVipTier()
+ {
+ return _vipTier;
+ }
+
+ public void setNewVipExpire(LocalDateTime e)
+ {
+ _vipExpire = e;
+ }
+
+ public LocalDateTime getNewVipExpire()
+ {
+ return _vipExpire;
+ }
+
+ public boolean isNewVip()
+ {
+ return _vipTier != null && _vipExpire != null && LocalDateTime.now().isBefore(_vipExpire);
+ }
+
}
\ No newline at end of file
diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java b/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java
index 2ce2d4a..07635f1 100644
--- a/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java
+++ b/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java
@@ -106,6 +106,7 @@
import TioPatinhas.BotPrevention.BotsPreventionManager;
import TioPatinhas.DollSystem.DollsData;
+import TioPatinhas.Vip.VipManager;
import hwid.Hwid;
import phantom.FakePlayer;
@@ -487,6 +488,8 @@
L2ClassMasterInstance.showQuestionMark(activeChar);
+ VipManager.getInstance().onLogin(activeChar);
+
// VIP Delete
if (activeChar.isVip())
{
diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/RequestEnchantItem.java b/java/net/sf/l2j/gameserver/network/clientpackets/RequestEnchantItem.java
index a1c9d5c..2e836f8 100644
--- a/java/net/sf/l2j/gameserver/network/clientpackets/RequestEnchantItem.java
+++ b/java/net/sf/l2j/gameserver/network/clientpackets/RequestEnchantItem.java
@@ -29,6 +29,8 @@
import net.sf.l2j.gameserver.util.Broadcast;
import net.sf.l2j.util.Rnd;
+import TioPatinhas.Vip.VipManager;
+
public final class RequestEnchantItem extends L2GameClientPacket
{
private int _objectId;
@@ -104,6 +106,28 @@
synchronized (item)
{
+
+ double chance = enchant.getChance(item);
+
+ // if (player != null && player.isNewVip())
+ if (player.isNewVip())
+ {
+ final int scrollId = scroll.getItemId();
+ final double mul = VipManager.getInstance().getEnchantMulRate(player, scrollId);
+ final double add = VipManager.getInstance().getEnchantAddPercent(player, scrollId);
+
+ if (mul != 1.0)
+ chance *= mul;
+ if (add != 0.0)
+ chance += add;
+
+ if (chance < 0.0)
+ chance = 0.0;
+ if (chance > 100.0)
+ chance = 100.0;
+ }
+
if (Rnd.get(100) < enchant.getChance(item)) // success
{
// announce the success
//================================== SQL ===========
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for character_vip_player
-- ----------------------------
DROP TABLE IF EXISTS `character_vip_player`;
CREATE TABLE `character_vip_player` (
`charId` int(11) NOT NULL,
`tier` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`expire_at` datetime NOT NULL,
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`charId`) USING BTREE,
INDEX `idx_character_vip_expire`(`expire_at` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of character_vip_player
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;