Skip to content

Instantly share code, notes, and snippets.

@VlLight
Last active October 17, 2016 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save VlLight/7777405 to your computer and use it in GitHub Desktop.
Save VlLight/7777405 to your computer and use it in GitHub Desktop.
Premium service
Index: dist/game/config/Services.properties
===================================================================
--- dist/game/config/Services.properties (revision 0)
+++ dist/game/config/Services.properties (working copy)
@@ -0,0 +1,55 @@
+# ---------------------------------------------------------------------------
+# Common Service Options
+# ---------------------------------------------------------------------------
+AllowServiceVoiced = False
+
+# ---------------------------------------------------------------------------
+# Premium System Options
+# ---------------------------------------------------------------------------
+# Allow premium service system. Check other options and rates carefully, before enable it!
+# Default: False
+PremiumServiceEnabled = False
+
+# Calculate drop and spoil in party by premium rules, if last attacker is not premium owner, but there is premium owner in party.
+# Default: False
+PremiumPartyDropSpoil = False
+
+# Show premium status (a square around player's level).
+# Default: True
+ShowPremiumStatus = True
+
+# Notify player about soon premium service expiration (one day before) on enter world.
+# Default: True
+NotifyPremiumExpiration = True
+
+# Activate premium service for newbies for given number of days. Put 0 to disable feature.
+# Default: 0.
+NewbiesPremiumPeriod = 0
+
+# Allow adding premium service using .addpremium voiced command
+PremiumAllowVoiced = False
+
+# ---------------------------------------------------------------------------
+# Premium Service Price
+# ---------------------------------------------------------------------------
+# Price of premium service. Format is timePeriod,item,itemCount[;timePeriod,item,itemCount]
+# Time period - a number and code letter (s - seconds, m - minutes, h - hours, d - days, M - months).
+# If itemCount is 0 - service is free. You can define both base period (i.e. 1 hour, 1 day) and multiplied period (3 hours, 5 days)
+AccountPremiumPrice = 1s,57,0;1m,57,0;1h,57,1;1d,57,24;1M,57,720
+CharPremiumPrice = 1s,57,0;1m,57,0;1h,57,1;1d,57,24;1M,57,720
+
+# ---------------------------------------------------------------------------
+# Premium Base Rates
+# ---------------------------------------------------------------------------
+# Experience multiplier
+PremiumRateXp = 2.
+# Skill points multiplier
+PremiumRateSp = 2.
+# Item's drop multiplier
+PremiumRateDropItems = 2.
+# Item's drop multiplier for Raid Bosses
+PremiumRateRaidDropItems = 1.
+# Item's spoil multiplier
+PremiumRateSpoil = 2.
+# List of items affected by custom drop rate by id, used now for Adena rate too.
+PremiumRateDropItemsById = 57,2
\ No newline at end of file
Index: java/com/l2jserver/Config.java
===================================================================
--- java/com/l2jserver/Config.java (revision 6303)
+++ java/com/l2jserver/Config.java (working copy)
@@ -52,6 +52,7 @@
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
+import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.enums.IllegalActionPunishmentType;
import com.l2jserver.gameserver.model.holders.ItemHolder;
@@ -104,6 +105,7 @@
public static final String CHAT_FILTER_FILE = "./config/chatfilter.txt";
public static final String EMAIL_CONFIG_FILE = "./config/Email.properties";
public static final String CH_SIEGE_FILE = "./config/ConquerableHallSiege.properties";
+ public static final String SERVICES_CONFIG_FILE = "./config/Services.properties";
// --------------------------------------------------
// L2J Variable Definitions
// --------------------------------------------------
@@ -1116,6 +1118,24 @@
public static int CHS_FAME_AMOUNT;
public static int CHS_FAME_FREQUENCY;
+ /** Services Settings **/
+ public static boolean ALLOW_SERVICE_VOICED;
+ public static boolean PREMIUM_SERVICE_ENABLED;
+ public static boolean PREMIUM_ALLOW_VOICED;
+ public static boolean PREMIUM_PARTY_DROPSPOIL;
+ public static Map<String, ItemHolder> CHAR_PREMIUM_PRICE;
+ public static Map<String, ItemHolder> ACCOUNT_PREMIUM_PRICE;
+ public static boolean SHOW_PREMIUM_STATUS;
+ public static int NEWBIES_PREMIUM_PERIOD;
+ public static boolean NOTIFY_PREMIUM_EXPIRATION;
+
+ public static float PREMIUM_RATE_XP;
+ public static float PREMIUM_RATE_SP;
+ public static float PREMIUM_RATE_SPOIL;
+ public static float PREMIUM_RATE_DROP_ITEMS;
+ public static float PREMIUM_RATE_DROP_ITEMS_BY_RAID;
+ public static Map<Integer, Float> PREMIUM_RATE_DROP_ITEMS_ID;
+
/**
* This class initializes all global variables for configuration.<br>
* If the key doesn't appear in properties file, a default value is set by this class. {@link #CONFIGURATION_FILE} (properties file) for configuring your server.
@@ -2063,7 +2083,7 @@
RAID_MIN_RESPAWN_MULTIPLIER = NPC.getFloat("RaidMinRespawnMultiplier", 1.0f);
RAID_MAX_RESPAWN_MULTIPLIER = NPC.getFloat("RaidMaxRespawnMultiplier", 1.0f);
RAID_MINION_RESPAWN_TIMER = NPC.getInt("RaidMinionRespawnTime", 300000);
- final String[] propertySplit = NPC.getString("CustomMinionsRespawnTime", "").split(";");
+ String[] propertySplit = NPC.getString("CustomMinionsRespawnTime", "").split(";");
MINIONS_RESPAWN_TIME = new HashMap<>(propertySplit.length);
for (String prop : propertySplit)
{
@@ -2762,6 +2782,141 @@
CHS_ENABLE_FAME = ClanHallSiege.getBoolean("EnableFame", false);
CHS_FAME_AMOUNT = ClanHallSiege.getInt("FameAmount", 0);
CHS_FAME_FREQUENCY = ClanHallSiege.getInt("FameFrequency", 0);
+
+ // Services properties
+ final PropertiesParser ServicesProperties = new PropertiesParser(SERVICES_CONFIG_FILE);
+
+ ALLOW_SERVICE_VOICED = ServicesProperties.getBoolean("AllowServiceVoiced", false);
+ PREMIUM_SERVICE_ENABLED = ServicesProperties.getBoolean("PremiumServiceEnabled", false);
+ PREMIUM_ALLOW_VOICED = ServicesProperties.getBoolean("PremiumAllowVoiced", false);
+ PREMIUM_PARTY_DROPSPOIL = ServicesProperties.getBoolean("PremiumPartyDropSpoil", false);
+ SHOW_PREMIUM_STATUS = ServicesProperties.getBoolean("ShowPremiumStatus", true);
+ NEWBIES_PREMIUM_PERIOD = ServicesProperties.getInt("NewbiesPremiumPeriod", 0);
+ NOTIFY_PREMIUM_EXPIRATION = ServicesProperties.getBoolean("NotifyPremiumExpiration", true);
+
+ PREMIUM_RATE_XP = ServicesProperties.getFloat("PremiumRateXp", 1);
+ PREMIUM_RATE_SP = ServicesProperties.getFloat("PremiumRateSp", 1);
+ PREMIUM_RATE_DROP_ITEMS = ServicesProperties.getFloat("PremiumRateDropItems", 1);
+ PREMIUM_RATE_DROP_ITEMS_BY_RAID = ServicesProperties.getFloat("PremiumRateRaidDropItems", 1);
+ PREMIUM_RATE_SPOIL = ServicesProperties.getFloat("PremiumRateSpoil", 1);
+
+ propertySplit = ServicesProperties.getString("PremiumRateDropItemsById", "").split(";");
+ PREMIUM_RATE_DROP_ITEMS_ID = new HashMap<>(propertySplit.length);
+ if (!propertySplit[0].isEmpty())
+ {
+ for (String item : propertySplit)
+ {
+ String[] itemSplit = item.split(",");
+ if (itemSplit.length != 2)
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid config property -> PremiumRateDropItemsById \"", item, "\""));
+ }
+ else
+ {
+ try
+ {
+ PREMIUM_RATE_DROP_ITEMS_ID.put(Integer.parseInt(itemSplit[0]), Float.parseFloat(itemSplit[1]));
+ }
+ catch (NumberFormatException nfe)
+ {
+ if (!item.isEmpty())
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid config property -> PremiumRateDropItemsById \"", item, "\""));
+ }
+ }
+ }
+ }
+ }
+ if (PREMIUM_RATE_DROP_ITEMS_ID.get(PcInventory.ADENA_ID) == 0f)
+ {
+ PREMIUM_RATE_DROP_ITEMS_ID.put(PcInventory.ADENA_ID, PREMIUM_RATE_DROP_ITEMS); // for Adena rate if not defined
+ }
+
+ propertySplit = ServicesProperties.getString("CharPremiumPrice", "").split(";");
+ if (!propertySplit[0].isEmpty())
+ {
+ CHAR_PREMIUM_PRICE = new HashMap<>();
+ for (String item : propertySplit)
+ {
+ String[] itemSplit = item.split(",");
+ if (itemSplit.length != 3)
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid config property -> PremiumPrice \"", item, "\""));
+ }
+ else
+ {
+ if (Util.toMillis(itemSplit[0]) <= 0) // check for correct time period
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid time period -> PremiumPrice \"", itemSplit[0], "\""));
+ }
+ else
+ {
+ int itemId;
+ long itemCount;
+ try
+ {
+ itemId = Integer.parseInt(itemSplit[1]);
+ itemCount = Long.parseLong(itemSplit[2]);
+ if ((itemCount > 0) && (ItemTable.getInstance().getTemplate(itemId) == null)) // Check for existing item, if count is greater, than 0
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid item id -> PremiumPrice \"", itemSplit[1], "\""));
+ }
+ else
+ {
+ CHAR_PREMIUM_PRICE.put(itemSplit[0], new ItemHolder(itemId, itemCount));
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid item parameters -> PremiumPrice \"", itemSplit[1] + ";" + itemSplit[2], "\""));
+ }
+ }
+ }
+ }
+ }
+
+ propertySplit = ServicesProperties.getString("AccountPremiumPrice", "").split(";");
+ if (!propertySplit[0].isEmpty())
+ {
+ ACCOUNT_PREMIUM_PRICE = new HashMap<>();
+ for (String item : propertySplit)
+ {
+ String[] itemSplit = item.split(",");
+ if (itemSplit.length != 3)
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid config property -> PremiumPrice \"", item, "\""));
+ }
+ else
+ {
+ if (Util.toMillis(itemSplit[0]) <= 0) // check for correct time period
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid time period -> PremiumPrice \"", itemSplit[0], "\""));
+ }
+ else
+ {
+ int itemId;
+ long itemCount;
+ try
+ {
+ itemId = Integer.parseInt(itemSplit[1]);
+ itemCount = Long.parseLong(itemSplit[2]);
+ if ((itemCount > 0) && (ItemTable.getInstance().getTemplate(itemId) == null)) // Check for existing item, if count is greater, than 0
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid item id -> PremiumPrice \"", itemSplit[1], "\""));
+ }
+ else
+ {
+ ACCOUNT_PREMIUM_PRICE.put(itemSplit[0], new ItemHolder(itemId, itemCount));
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ _log.warning(StringUtil.concat("Config.load(): invalid item parameters -> PremiumPrice \"", itemSplit[1] + ";" + itemSplit[2], "\""));
+ }
+ }
+ }
+ }
+ }
}
else if (Server.serverMode == Server.MODE_LOGINSERVER)
{
Index: java/com/l2jserver/gameserver/datatables/PremiumTable.java
===================================================================
--- java/com/l2jserver/gameserver/datatables/PremiumTable.java (revision 0)
+++ java/com/l2jserver/gameserver/datatables/PremiumTable.java (working copy)
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.datatables;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Date;
+import java.util.logging.Logger;
+
+import com.l2jserver.Config;
+import com.l2jserver.L2DatabaseFactory;
+import com.l2jserver.gameserver.enums.ServiceType;
+import com.l2jserver.gameserver.instancemanager.ExpirableServicesManager;
+import com.l2jserver.gameserver.model.ExpirableService;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.holders.ItemHolder;
+import com.l2jserver.gameserver.network.serverpackets.ExBrPremiumState;
+import com.l2jserver.gameserver.scripting.scriptengine.listeners.player.PlayerDespawnListener;
+import com.l2jserver.gameserver.scripting.scriptengine.listeners.player.PlayerSpawnListener;
+import com.l2jserver.gameserver.util.TimeConstant;
+import com.l2jserver.gameserver.util.Util;
+
+/**
+ * This class manages storing of premium service inforamtion.
+ * @author GKR
+ */
+
+public class PremiumTable
+{
+ private static Logger _log = Logger.getLogger(PremiumTable.class.getName());
+
+ public static float PREMIUM_BONUS_EXP = 1;
+ public static float PREMIUM_BONUS_SP = 1;
+
+ // SQL DEFINITIONS
+ private static String LOAD_ACCOUNT_RECORD = "SELECT expiration FROM account_services WHERE charLogin = ? AND serviceName = ?";
+ private static String LOAD_CHAR_RECORD = "SELECT expiration FROM character_services WHERE charId = ? AND serviceName = ?";
+ private static String INSERT_ACCOUNT_RECORD = "REPLACE INTO account_services(charLogin, serviceName, expiration) VALUES (?,?,?)";
+ private static String INSERT_CHAR_RECORD = "REPLACE INTO character_services(charId, serviceName, expiration) VALUES (?,?,?)";
+ private static String REMOVE_RECORD = "DELETE FROM character_services WHERE charId = ? AND serviceName = ?";
+
+ public PremiumTable()
+ {
+ if (Config.PREMIUM_SERVICE_ENABLED)
+ {
+ registerListeners();
+
+ PREMIUM_BONUS_EXP = Math.max((Config.PREMIUM_RATE_XP / Config.RATE_XP), 1);
+ PREMIUM_BONUS_SP = Math.max((Config.PREMIUM_RATE_SP / Config.RATE_SP), 1);
+ }
+ }
+
+ /**
+ * Register listeners for player's enter world and logout
+ */
+ private void registerListeners()
+ {
+ new PlayerSpawnListener()
+ {
+ /**
+ * Send Premium State packet, to player with enabled premium service; send notification, if premium service will expire soon
+ * @param player player to send info
+ */
+ @Override
+ public void onPlayerLogin(L2PcInstance player)
+ {
+ if (Config.NOTIFY_PREMIUM_EXPIRATION && player.hasPremium() && !ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).isUnlimited())
+ {
+ Date testDate = new Date(ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).getExpirationDate());
+ if (Util.isToday(testDate))
+ {
+ player.sendMessage("Your premium will expire today");
+ }
+ else if (Util.isTomorrow(testDate))
+ {
+ player.sendMessage("Your premium will expire tomorrow");
+ }
+ }
+
+ // Turn off status here too, because ExBrPremiumState doesn't work at logout
+ if (Config.SHOW_PREMIUM_STATUS)
+ {
+ player.sendPacket(new ExBrPremiumState(player.getObjectId(), (player.hasPremium() ? 1 : 0)));
+ }
+ }
+ };
+
+ /**
+ * Unregister player's premium service at logout
+ * @param player player to send info
+ */
+ new PlayerDespawnListener()
+ {
+ @Override
+ public void onPlayerLogout(L2PcInstance player)
+ {
+ if (player.hasPremium())
+ {
+ ExpirableServicesManager.getInstance().expireService(ServiceType.PREMIUM, player);
+ }
+ }
+ };
+ }
+
+ /**
+ * Load player's premium state from database
+ * @param player player to load info
+ */
+ public static void loadState(L2PcInstance player)
+ {
+ if (!Config.PREMIUM_SERVICE_ENABLED || (player == null))
+ {
+ return;
+ }
+
+ long expirationDate = 0;
+ boolean isAccountBased = false;
+
+ // Check account settings first
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement statement = con.prepareStatement(LOAD_ACCOUNT_RECORD))
+ {
+ statement.setString(1, player.getAccountName());
+ statement.setString(2, ServiceType.PREMIUM.toString());
+ ResultSet rset = statement.executeQuery();
+
+ if (rset.next())
+ {
+ expirationDate = rset.getLong("expiration");
+ isAccountBased = true;
+ }
+ rset.close();
+ }
+ catch (Exception e)
+ {
+ _log.warning(PremiumTable.class.getName() + ": Error while loading account premium data for character " + player.getName() + ", account: " + player.getAccountName() + ": " + e);
+ }
+
+ // check personal data, if account data either is not available, or expired
+ if ((expirationDate == 0) || (expirationDate < System.currentTimeMillis()))
+ {
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement statement = con.prepareStatement(LOAD_CHAR_RECORD))
+ {
+ statement.setInt(1, player.getObjectId());
+ statement.setString(2, ServiceType.PREMIUM.toString());
+ ResultSet rset = statement.executeQuery();
+
+ if (rset.next())
+ {
+ expirationDate = rset.getLong("expiration");
+ }
+ rset.close();
+ }
+ catch (Exception e)
+ {
+ _log.warning(PremiumTable.class.getName() + ": Error while loading character premium data for character " + player.getName() + ": " + e);
+ }
+ }
+
+ // Register service, if exists and isn't expired
+ if ((expirationDate < 0) || (expirationDate > System.currentTimeMillis()))
+ {
+ ExpirableServicesManager.getInstance().registerService(player, new ExpirableService(ServiceType.PREMIUM, expirationDate, isAccountBased));
+ }
+ }
+
+ /**
+ * Extends premium period for given time
+ * @param player player to operate
+ * @param millisCount time in milliseconds
+ * @param unlimited {@code true} if service should be time-unlimited
+ * @param isAccountBased {@code true} if service is account-based, {@code false} if service is character-based
+ * @param store {@code true} if premium state should be stored in database, {@code false} if it will expire at logout
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ private static boolean addTime(L2PcInstance player, long millisCount, boolean unlimited, boolean isAccountBased, boolean store)
+ {
+ if (!Config.PREMIUM_SERVICE_ENABLED || (player == null))
+ {
+ return false;
+ }
+
+ // Unlimited premium service will newer touched. There is also no need to touch parameter, if no-store is choosen for already enabled service
+ if (ExpirableServicesManager.getInstance().hasService(ServiceType.PREMIUM, player) && (!store || ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).isUnlimited()))
+ {
+ return false;
+ }
+
+ long expirationDate;
+ boolean success = !store;
+
+ if (unlimited)
+ {
+ expirationDate = -1;
+ }
+
+ else if (!store)
+ {
+ expirationDate = 0;
+ }
+
+ else
+ {
+ expirationDate = ExpirableServicesManager.getInstance().hasService(ServiceType.PREMIUM, player) ? ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).getExpirationDate() : 0;
+ expirationDate = Math.max(expirationDate, System.currentTimeMillis()) + millisCount;
+ }
+
+ // Store info in database, if needed
+ if (store)
+ {
+ if (isAccountBased)
+ {
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement statement = con.prepareStatement(INSERT_ACCOUNT_RECORD))
+ {
+ statement.setString(1, player.getAccountName());
+ statement.setString(2, ServiceType.PREMIUM.toString());
+ statement.setLong(3, expirationDate);
+ statement.executeUpdate();
+ success = true;
+ }
+ catch (Exception e)
+ {
+ _log.warning(PremiumTable.class.getName() + ": Could not save data for account " + player.getAccountName() + ", player " + player.getName() + ": " + e);
+ }
+ }
+ else
+ {
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement statement = con.prepareStatement(INSERT_CHAR_RECORD))
+ {
+ statement.setInt(1, player.getObjectId());
+ statement.setString(2, ServiceType.PREMIUM.toString());
+ statement.setLong(3, expirationDate);
+ statement.executeUpdate();
+ success = true;
+ }
+ catch (Exception e)
+ {
+ _log.warning(PremiumTable.class.getName() + ": Could not save data for character " + player.getName() + ": " + e);
+ }
+ }
+ }
+
+ // if store was successfull, or store doesn't required
+ if (success)
+ {
+ // register service
+ ExpirableServicesManager.getInstance().registerService(player, new ExpirableService(ServiceType.PREMIUM, expirationDate, isAccountBased));
+
+ // Send Premium packet, if needed
+ if (Config.SHOW_PREMIUM_STATUS)
+ {
+ player.sendPacket(new ExBrPremiumState(player.getObjectId(), 1));
+ }
+
+ // Send text message
+ player.sendMessage("Premium service is activated");
+ }
+
+ return success;
+ }
+
+ /**
+ * Wrapper for {@link #addTime(L2PcInstance, long, boolean, boolean, boolean)}. Extends premium period for given time, store state in database
+ * @param player player to operate
+ * @param millisCount time in milliseconds
+ * @param isAccountBased {@code true} if service is account-based, {@code false} if service is character-based
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean addTime(L2PcInstance player, long millisCount, boolean isAccountBased)
+ {
+ return addTime(player, millisCount, false, isAccountBased, true);
+ }
+
+ /**
+ * Wrapper for {@link #addTime(L2PcInstance, long, boolean, boolean, boolean)}. Extends premium period for given time, store state in database
+ * @param player player to operate
+ * @param millisCount time in milliseconds
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean addTime(L2PcInstance player, long millisCount)
+ {
+ if ((millisCount <= 0) || !ExpirableServicesManager.getInstance().hasService(ServiceType.PREMIUM, player))
+ {
+ return false;
+ }
+
+ return addTime(player, millisCount, ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).isAccountBased());
+ }
+
+ /**
+ * Wrapper for {@link #addTime(L2PcInstance, long, boolean, boolean, boolean)}. Extends premium period for given time, store state in database
+ * @param player player to operate
+ * @param val type of time period
+ * @param count number of given period
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean addTime(L2PcInstance player, TimeConstant val, int count)
+ {
+ return addTime(player, val.getTimeInMillis() * count);
+ }
+
+ /**
+ * Wrapper for {@link #addTime(L2PcInstance, long, boolean, boolean, boolean)}. Extends premium period for unlimited time, store state in database
+ * @param player player to operate
+ * @param isAccountBased {@code true} if service is account-based, {@code false} if service is character-based
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean setUnlimitedPremium(L2PcInstance player, boolean isAccountBased)
+ {
+ return addTime(player, 0, true, isAccountBased, true);
+ }
+
+ /**
+ * Wrapper for {@link #addTime(L2PcInstance, long, boolean, boolean, boolean)}. Activates account premium service for player - give temporary premium, expiring at logout
+ * @param player player to operate
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean setTemporaryPremium(L2PcInstance player)
+ {
+ return addTime(player, 0, false, false, false);
+ }
+
+ /**
+ * Unregister premium service from given player and remove it from database
+ * @param player player to operate
+ */
+ public static void removeService(L2PcInstance player)
+ {
+ if ((player == null) || !player.hasPremium())
+ {
+ return;
+ }
+
+ try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+ PreparedStatement statement = con.prepareStatement(REMOVE_RECORD))
+ {
+ statement.setInt(1, player.getObjectId());
+ statement.setString(2, ServiceType.PREMIUM.toString());
+ statement.execute();
+ }
+ catch (Exception e)
+ {
+ _log.warning(PremiumTable.class.getName() + ": Could not remove data for character " + player.getName() + ": " + e);
+ }
+
+ ExpirableServicesManager.getInstance().expireService(ServiceType.PREMIUM, player);
+ }
+
+ /**
+ * Gets price of given premium period
+ * @param period period to calculate
+ * @param isAccountBased {@code true} if service is account-based, {@code false} if service is character-based
+ * @return correspondent ItemHolder for given period and base
+ */
+ public static ItemHolder getPrice(String period, boolean isAccountBased)
+ {
+ return isAccountBased ? Config.ACCOUNT_PREMIUM_PRICE.get(period) : Config.CHAR_PREMIUM_PRICE.get(period);
+ }
+
+ /**
+ * Activates premium service for player
+ * @param player player to activate
+ * @param period time period for activation
+ * @param seller reference character (for logging puproses)
+ * @param isAccountBased {@code true} if service is account-based, {@code false} if service is character-based
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ private static boolean givePremium(L2PcInstance player, String period, L2Character seller, boolean isAccountBased)
+ {
+ ItemHolder payItem = getPrice(period, isAccountBased);
+ if ((player == null) || !player.isOnline() || (payItem == null))
+ {
+ if (payItem == null)
+ {
+ _log.warning(PremiumTable.class.getName() + ": No payitem is defined for period " + period);
+ }
+ return false;
+ }
+
+ // do not allow player to buy another type of service (character VS account), if already has one
+ if (ExpirableServicesManager.getInstance().hasService(ServiceType.PREMIUM, player) && (ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).isAccountBased() != isAccountBased))
+ {
+ player.sendMessage("Incompatible premium modes");
+ }
+
+ else if ((payItem.getCount() > 0) && (player.getInventory().getInventoryItemCount(payItem.getId(), -1, false) < payItem.getCount()))
+ {
+ player.sendMessage("Not enough items to use");
+ }
+
+ else if (addTime(player, Util.toMillis(period), isAccountBased))
+ {
+ player.destroyItemByItemId("Premium service", payItem.getId(), payItem.getCount(), seller, true);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Wrapper for {@link #givePremium(L2PcInstance, String, L2Character, boolean)}. Activates account premium service for player
+ * @param player player to activate
+ * @param period time period for activation
+ * @param seller reference character (for logging puproses)
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean giveAccountPremium(L2PcInstance player, String period, L2Character seller)
+ {
+ return givePremium(player, period, seller, true);
+ }
+
+ /**
+ * Wrapper for {@link #givePremium(L2PcInstance, String, L2Character, boolean)}. Activates character premium service for player
+ * @param player player to activate
+ * @param period time period for activation
+ * @param seller reference character (for logging puproses)
+ * @return {@code true} if operation is successfull, {@code false} otherwise
+ */
+ public static boolean giveCharPremium(L2PcInstance player, String period, L2Character seller)
+ {
+ return givePremium(player, period, seller, false);
+ }
+
+ public static final PremiumTable getInstance()
+ {
+ return SingletonHolder._instance;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final PremiumTable _instance = new PremiumTable();
+ }
+}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/enums/Rates.java
===================================================================
--- java/com/l2jserver/gameserver/enums/Rates.java (revision 0)
+++ java/com/l2jserver/gameserver/enums/Rates.java (working copy)
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.enums;
+
+/**
+ * Enum of some rates.
+ * @author GKR
+ */
+public enum Rates
+{
+ PREMIUM_BONUS_EXP,
+ PREMIUM_BONUS_SP,
+ SPOIL,
+ DROP_ITEM;
+}
Index: java/com/l2jserver/gameserver/enums/ServiceType.java
===================================================================
--- java/com/l2jserver/gameserver/enums/ServiceType.java (revision 0)
+++ java/com/l2jserver/gameserver/enums/ServiceType.java (working copy)
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.enums;
+/**
+ * Contains possible service types
+ * @author GKR
+ */
+public enum ServiceType
+{
+ PREMIUM;
+}
Index: java/com/l2jserver/gameserver/GameServer.java
===================================================================
--- java/com/l2jserver/gameserver/GameServer.java (revision 6303)
+++ java/com/l2jserver/gameserver/GameServer.java (working copy)
@@ -74,6 +74,7 @@
import com.l2jserver.gameserver.datatables.OfflineTradersTable;
import com.l2jserver.gameserver.datatables.OptionsData;
import com.l2jserver.gameserver.datatables.PetDataTable;
+import com.l2jserver.gameserver.datatables.PremiumTable;
import com.l2jserver.gameserver.datatables.RecipeData;
import com.l2jserver.gameserver.datatables.SecondaryAuthData;
import com.l2jserver.gameserver.datatables.SkillLearnData;
@@ -100,6 +101,7 @@
import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager;
+import com.l2jserver.gameserver.instancemanager.ExpirableServicesManager;
import com.l2jserver.gameserver.instancemanager.FortManager;
import com.l2jserver.gameserver.instancemanager.FortSiegeManager;
import com.l2jserver.gameserver.instancemanager.FourSepulchersManager;
@@ -256,6 +258,8 @@
RaidBossPointsManager.getInstance();
PetDataTable.getInstance();
CharSummonTable.getInstance().init();
+ PremiumTable.getInstance();
+ ExpirableServicesManager.getInstance();
printSection("Clans");
ClanTable.getInstance();
Index: java/com/l2jserver/gameserver/idfactory/IdFactory.java
===================================================================
--- java/com/l2jserver/gameserver/idfactory/IdFactory.java (revision 6303)
+++ java/com/l2jserver/gameserver/idfactory/IdFactory.java (working copy)
@@ -123,7 +123,8 @@
private static final String[] TIMESTAMPS_CLEAN =
{
"DELETE FROM character_instance_time WHERE time <= ?",
- "DELETE FROM character_skills_save WHERE restore_type = 1 AND systime <= ?"
+ "DELETE FROM character_skills_save WHERE restore_type = 1 AND systime <= ?",
+ "DELETE FROM character_services WHERE expiration <= ? AND expiration >= 0"
};
protected boolean _initialized;
@@ -260,6 +261,7 @@
cleanCount += stmt.executeUpdate("DELETE FROM character_quest_global_data WHERE character_quest_global_data.charId NOT IN (SELECT charId FROM characters);");
cleanCount += stmt.executeUpdate("DELETE FROM character_tpbookmark WHERE character_tpbookmark.charId NOT IN (SELECT charId FROM characters);");
cleanCount += stmt.executeUpdate("DELETE FROM character_variables WHERE character_variables.charId NOT IN (SELECT charId FROM characters);");
+ cleanCount += stmt.executeUpdate("DELETE FROM character_services WHERE character_services.charId NOT IN (SELECT charId FROM characters);");
// If the clan does not exist...
cleanCount += stmt.executeUpdate("DELETE FROM clan_privs WHERE clan_privs.clan_id NOT IN (SELECT clan_id FROM clan_data);");
Index: java/com/l2jserver/gameserver/instancemanager/ExpirableServicesManager.java
===================================================================
--- java/com/l2jserver/gameserver/instancemanager/ExpirableServicesManager.java (revision 0)
+++ java/com/l2jserver/gameserver/instancemanager/ExpirableServicesManager.java (working copy)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.instancemanager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javolution.util.FastMap;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.enums.ServiceType;
+import com.l2jserver.gameserver.model.ExpirableService;
+import com.l2jserver.gameserver.model.L2World;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.serverpackets.ExBrPremiumState;
+
+/**
+ * This class manages time expirable services.
+ * @author GKR
+ */
+
+public class ExpirableServicesManager
+{
+ private final Map<ServiceType, Map<Integer, ExpirableService>> _holder; // store service maps
+
+ protected ExpirableServicesManager()
+ {
+ _holder = new HashMap<>();
+ for (ServiceType type : ServiceType.values())
+ {
+ _holder.put(type, new FastMap<Integer, ExpirableService>().shared());
+ }
+ }
+
+ /**
+ * Register service for player in expiration date holder
+ * @param player player to register
+ * @param service type of service
+ */
+ public void registerService(L2PcInstance player, ExpirableService service)
+ {
+ _holder.get(service.getType()).put(player.getObjectId(), service);
+ }
+
+ public ExpirableService getService(ServiceType type, L2PcInstance player)
+ {
+ return _holder.get(type).get(player.getObjectId());
+ }
+
+ /**
+ * @param type type of service
+ * @param player to check
+ * @return {@code true} if given service type is registered for given player, {@code false} otherwise
+ */
+ public boolean hasService(ServiceType type, L2PcInstance player)
+ {
+ return _holder.get(type).containsKey(player.getObjectId());
+ }
+
+ /**
+ * Send message and premium state packet to player with given id, when service of given type is expiring
+ * @param type type of service
+ * @param playerId objectId of player to send info
+ */
+ private void expireService(ServiceType type, int playerId)
+ {
+ L2PcInstance player = L2World.getInstance().getPlayer(playerId);
+ if ((player != null) && player.isOnline())
+ {
+ switch (type)
+ {
+ case PREMIUM:
+ {
+ player.sendMessage("Premium service is expired");
+ if (Config.SHOW_PREMIUM_STATUS)
+ {
+ player.sendPacket(new ExBrPremiumState(player.getObjectId(), 0));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Unregister service of given type for given player
+ * @param type type of service
+ * @param player to process
+ */
+ public void expireService(ServiceType type, L2PcInstance player)
+ {
+ _holder.get(type).remove(player.getObjectId());
+ expireService(type, player.getObjectId()); // show appropriate things
+ }
+
+ /**
+ * Iterate over service stores, check for service expiration and unregister expired services
+ */
+ public void checkExpiration()
+ {
+ if (Config.PREMIUM_SERVICE_ENABLED)
+ {
+ for (ServiceType type : _holder.keySet())
+ {
+ Map<Integer, ExpirableService> charMap = _holder.get(type);
+ for (int charId : charMap.keySet())
+ {
+ if (charMap.get(charId).isExpired()) // Do not touch unlimited and temporary services
+ {
+ expireService(type, charId);
+ charMap.remove(charId);
+ }
+ }
+ }
+ }
+ }
+
+ public static final ExpirableServicesManager getInstance()
+ {
+ return SingletonHolder._instance;
+ }
+
+ private static class SingletonHolder
+ {
+ protected static final ExpirableServicesManager _instance = new ExpirableServicesManager();
+ }
+}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java (revision 6303)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java (working copy)
@@ -83,6 +83,7 @@
import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.PetDataTable;
+import com.l2jserver.gameserver.datatables.PremiumTable;
import com.l2jserver.gameserver.datatables.RecipeData;
import com.l2jserver.gameserver.datatables.SkillTable;
import com.l2jserver.gameserver.datatables.SkillTable.FrequentSkill;
@@ -94,6 +95,8 @@
import com.l2jserver.gameserver.enums.MountType;
import com.l2jserver.gameserver.enums.PcRace;
import com.l2jserver.gameserver.enums.QuestEventType;
+import com.l2jserver.gameserver.enums.Rates;
+import com.l2jserver.gameserver.enums.ServiceType;
import com.l2jserver.gameserver.enums.Sex;
import com.l2jserver.gameserver.enums.ShotType;
import com.l2jserver.gameserver.handler.IItemHandler;
@@ -105,6 +108,7 @@
import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager;
import com.l2jserver.gameserver.instancemanager.DuelManager;
+import com.l2jserver.gameserver.instancemanager.ExpirableServicesManager;
import com.l2jserver.gameserver.instancemanager.FortManager;
import com.l2jserver.gameserver.instancemanager.FortSiegeManager;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
@@ -326,6 +330,7 @@
import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
import com.l2jserver.gameserver.util.Broadcast;
import com.l2jserver.gameserver.util.FloodProtectors;
+import com.l2jserver.gameserver.util.TimeConstant;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
@@ -936,6 +941,11 @@
player.setNewbie(1);
// Give 20 recommendations
player.setRecomLeft(20);
+ //Give premium to Newbie, if needed
+ if (Config.NEWBIES_PREMIUM_PERIOD > 0)
+ {
+ PremiumTable.addTime(player, TimeConstant.DAY, Config.NEWBIES_PREMIUM_PERIOD);
+ }
// Add the player in the characters table of the database
return player.createDb() ? player : null;
}
@@ -7377,6 +7387,7 @@
}
player.restoreZoneRestartLimitTime();
+ PremiumTable.loadState(player); // restore premium status
if (player.isGM())
{
@@ -14935,4 +14946,103 @@
{
return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.PARTY_BAN);
}
+
+ /**
+ * @return {@code true} if premium service is registered for player, {@code false} otherwise
+ */
+ public boolean hasPremium()
+ {
+ return ExpirableServicesManager.getInstance().hasService(ServiceType.PREMIUM, this);
+ }
+
+ /**
+ * @param rateType type of rate to check
+ * @param itemId item id to check
+ * @param isRaid {@code true} if rate should be calculated against Raid boss
+ * @return rate value for given rate type and item id
+ */
+ public float getRate(Rates rateType, int itemId, boolean isRaid)
+ {
+ float rate = 0;
+ switch(rateType)
+ {
+ case PREMIUM_BONUS_EXP:
+ {
+ rate = hasPremium() ? PremiumTable.PREMIUM_BONUS_EXP : 1;
+ break;
+ }
+ case PREMIUM_BONUS_SP:
+ {
+ rate = hasPremium() ? PremiumTable.PREMIUM_BONUS_SP : 1;
+ break;
+ }
+ case SPOIL:
+ case DROP_ITEM:
+ {
+ // check for premium owner in party, if enabled by config
+ boolean hasPremium = false;
+ if (Config.PREMIUM_PARTY_DROPSPOIL && !hasPremium() && isInParty())
+ {
+ for (L2PcInstance pl : getParty().getMembers())
+ {
+ if (pl.hasPremium() && Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, pl, true))
+ {
+ hasPremium = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ hasPremium = hasPremium();
+ }
+
+ if (rateType == Rates.SPOIL)
+ {
+ rate = hasPremium ? Config.PREMIUM_RATE_SPOIL : Config.RATE_DROP_SPOIL;
+ }
+ else
+ {
+ if (hasPremium)
+ {
+ if (Config.PREMIUM_RATE_DROP_ITEMS_ID.containsKey(itemId)) // check for overriden rate in premium list first
+ {
+ rate = Config.PREMIUM_RATE_DROP_ITEMS_ID.get(itemId);
+ }
+ else if (Config.RATE_DROP_ITEMS_ID.containsKey(itemId)) // then check for overriden rate in general list
+ {
+ rate = Config.RATE_DROP_ITEMS_ID.get(itemId);
+ }
+ else // return premium rate, either for raid, or normal mob, if it isn't overriden anywhere
+ {
+ rate = isRaid ? Config.PREMIUM_RATE_DROP_ITEMS_BY_RAID : Config.PREMIUM_RATE_DROP_ITEMS;
+ }
+ }
+ else
+ {
+ if (Config.RATE_DROP_ITEMS_ID.containsKey(itemId)) // check for overriden rate in general list first
+ {
+ rate = Config.RATE_DROP_ITEMS_ID.get(itemId);
+ }
+ else // return general rate, either for raid, or normal mob, if it isn't overriden anywhere
+ {
+ rate = isRaid ? Config.RATE_DROP_ITEMS_BY_RAID : Config.RATE_DROP_ITEMS;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return rate;
+ }
+
+ /**
+ * @param rateType type of rate to check
+ * @return rate value for given rate type
+ */
+ public float getRate(Rates rateType)
+ {
+ return getRate(rateType, -1, false);
+ }
}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/model/actor/L2Attackable.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/L2Attackable.java (revision 6303)
+++ java/com/l2jserver/gameserver/model/actor/L2Attackable.java (working copy)
@@ -41,6 +41,7 @@
import com.l2jserver.gameserver.datatables.ManorData;
import com.l2jserver.gameserver.enums.InstanceType;
import com.l2jserver.gameserver.enums.QuestEventType;
+import com.l2jserver.gameserver.enums.Rates;
import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jserver.gameserver.instancemanager.WalkingManager;
import com.l2jserver.gameserver.model.AbsorberInfo;
@@ -1014,7 +1015,7 @@
deepBlueDrop = 3;
if (drop.getItemId() == PcInventory.ADENA_ID)
{
- deepBlueDrop *= isRaid() && !isRaidMinion() ? (int) Config.RATE_DROP_ITEMS_BY_RAID : (int) Config.RATE_DROP_ITEMS;
+ deepBlueDrop *= (int) lastAttacker.getRate(Rates.DROP_ITEM, PcInventory.ADENA_ID, (isRaid() && !isRaidMinion()));
}
}
}
@@ -1032,18 +1033,7 @@
}
// Applies Drop rates
- if (Config.RATE_DROP_ITEMS_ID.containsKey(drop.getItemId()))
- {
- dropChance *= Config.RATE_DROP_ITEMS_ID.get(drop.getItemId());
- }
- else if (isSweep)
- {
- dropChance *= Config.RATE_DROP_SPOIL;
- }
- else
- {
- dropChance *= isRaid() && !isRaidMinion() ? Config.RATE_DROP_ITEMS_BY_RAID : Config.RATE_DROP_ITEMS;
- }
+ dropChance *= lastAttacker.getRate(isSweep ? Rates.SPOIL : Rates.DROP_ITEM, drop.getItemId(), (isRaid() && !isRaidMinion()));
if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
{
@@ -1161,7 +1151,7 @@
}
// Applies Drop rates
- categoryDropChance *= isRaid() && !isRaidMinion() ? Config.RATE_DROP_ITEMS_BY_RAID : Config.RATE_DROP_ITEMS;
+ categoryDropChance *= lastAttacker.getRate(Rates.DROP_ITEM, -1, (isRaid() && !isRaidMinion()));
if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
{
@@ -1195,18 +1185,8 @@
// chance to give a 4th time.
// At least 1 item will be dropped for sure. So the chance will be adjusted to 100%
// if smaller.
+ double dropChance = drop.getChance() * lastAttacker.getRate(Rates.DROP_ITEM, drop.getItemId(), (isRaid() && !isRaidMinion()));
- double dropChance = drop.getChance();
-
- if (Config.RATE_DROP_ITEMS_ID.containsKey(drop.getItemId()))
- {
- dropChance *= Config.RATE_DROP_ITEMS_ID.get(drop.getItemId());
- }
- else
- {
- dropChance *= isRaid() && !isRaidMinion() ? Config.RATE_DROP_ITEMS_BY_RAID : Config.RATE_DROP_ITEMS;
- }
-
if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
{
dropChance *= Config.L2JMOD_CHAMPION_REWARDS;
Index: java/com/l2jserver/gameserver/model/actor/stat/PcStat.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/stat/PcStat.java (revision 6303)
+++ java/com/l2jserver/gameserver/model/actor/stat/PcStat.java (working copy)
@@ -22,6 +22,7 @@
import com.l2jserver.gameserver.datatables.ExperienceTable;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.PetDataTable;
+import com.l2jserver.gameserver.enums.Rates;
import com.l2jserver.gameserver.model.L2PetLevelData;
import com.l2jserver.gameserver.model.PcCondOverride;
import com.l2jserver.gameserver.model.actor.instance.L2ClassMasterInstance;
@@ -828,6 +829,9 @@
bonus += (bonusExp - 1);
}
+ // Apply premium bonus
+ bonus *= getActiveChar().getRate(Rates.PREMIUM_BONUS_EXP);
+
// Check for abnormal bonuses
bonus = Math.max(bonus, 1);
bonus = Math.min(bonus, Config.MAX_BONUS_EXP);
@@ -872,6 +876,9 @@
bonus += (bonusSp - 1);
}
+ // Apply premium bonus
+ bonus *= getActiveChar().getRate(Rates.PREMIUM_BONUS_SP);
+
// Check for abnormal bonuses
bonus = Math.max(bonus, 1);
bonus = Math.min(bonus, Config.MAX_BONUS_SP);
Index: java/com/l2jserver/gameserver/model/ExpirableService.java
===================================================================
--- java/com/l2jserver/gameserver/model/ExpirableService.java (revision 0)
+++ java/com/l2jserver/gameserver/model/ExpirableService.java (working copy)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.model;
+
+import com.l2jserver.gameserver.enums.ServiceType;
+
+/**
+ * This class holds info about expirable services.
+ * @author GKR
+ */
+public final class ExpirableService
+{
+ private ServiceType _type;
+ private long _expirationDate;
+ private boolean _isAccountBased;
+
+ public ExpirableService(ServiceType type, long expirationDate, boolean isAccountBased)
+ {
+ _type = type;
+ _expirationDate = expirationDate;
+ _isAccountBased = isAccountBased;
+ }
+
+ public void setExpirationDate(long date)
+ {
+ _expirationDate = date;
+ }
+
+ public ServiceType getType()
+ {
+ return _type;
+ }
+
+ public long getExpirationDate()
+ {
+ return _expirationDate;
+ }
+
+ public boolean isTypeOf(ServiceType type)
+ {
+ return (type == _type);
+ }
+
+ public boolean isExpired()
+ {
+ return ((_expirationDate > 0) && (_expirationDate <= System.currentTimeMillis()));
+ }
+
+ public boolean isTemporary()
+ {
+ return (_expirationDate == 0);
+ }
+
+ public boolean isUnlimited()
+ {
+ return (_expirationDate == -1);
+ }
+
+ public boolean isAccountBased()
+ {
+ return _isAccountBased;
+ }
+}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/taskmanager/TaskManager.java
===================================================================
--- java/com/l2jserver/gameserver/taskmanager/TaskManager.java (revision 6303)
+++ java/com/l2jserver/gameserver/taskmanager/TaskManager.java (working copy)
@@ -47,6 +47,7 @@
import com.l2jserver.gameserver.taskmanager.tasks.TaskRaidPointsReset;
import com.l2jserver.gameserver.taskmanager.tasks.TaskRecom;
import com.l2jserver.gameserver.taskmanager.tasks.TaskRestart;
+import com.l2jserver.gameserver.taskmanager.tasks.TaskServiceExpire;
import com.l2jserver.gameserver.taskmanager.tasks.TaskScript;
import com.l2jserver.gameserver.taskmanager.tasks.TaskSevenSignsUpdate;
import com.l2jserver.gameserver.taskmanager.tasks.TaskShutdown;
@@ -195,6 +196,7 @@
registerTask(new TaskRaidPointsReset());
registerTask(new TaskRecom());
registerTask(new TaskRestart());
+ registerTask(new TaskServiceExpire());
registerTask(new TaskScript());
registerTask(new TaskSevenSignsUpdate());
registerTask(new TaskShutdown());
Index: java/com/l2jserver/gameserver/taskmanager/tasks/TaskServiceExpire.java
===================================================================
--- java/com/l2jserver/gameserver/taskmanager/tasks/TaskServiceExpire.java (revision 0)
+++ java/com/l2jserver/gameserver/taskmanager/tasks/TaskServiceExpire.java (working copy)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.taskmanager.tasks;
+
+import com.l2jserver.gameserver.instancemanager.ExpirableServicesManager;
+import com.l2jserver.gameserver.taskmanager.Task;
+import com.l2jserver.gameserver.taskmanager.TaskManager;
+import com.l2jserver.gameserver.taskmanager.TaskManager.ExecutedTask;
+import com.l2jserver.gameserver.taskmanager.TaskTypes;
+
+/**
+ * @author GKR
+ */
+public class TaskServiceExpire extends Task
+{
+ public static final String NAME = "service_expire";
+
+ @Override
+ public String getName()
+ {
+ return NAME;
+ }
+
+ @Override
+ public void onTimeElapsed(ExecutedTask task)
+ {
+ ExpirableServicesManager.getInstance().checkExpiration();
+ }
+
+ @Override
+ public void initializate()
+ {
+ super.initializate();
+ TaskManager.addUniqueTask(NAME, TaskTypes.TYPE_FIXED_SHEDULED, "600000", "600000", "");
+ }
+}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/util/TimeConstant.java
===================================================================
--- java/com/l2jserver/gameserver/util/TimeConstant.java (revision 0)
+++ java/com/l2jserver/gameserver/util/TimeConstant.java (working copy)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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.l2jserver.gameserver.util;
+
+/**
+ * Hold number of milliseconds for wide-using time periods
+ * @author GKR
+ */
+
+public enum TimeConstant
+{
+ NONE(-1L, "", ""),
+ SECOND(1000L, "second", "s"),
+ MINUTE(60000L, "minute", "m"),
+ HOUR(3600000L, "hour", "h"),
+ DAY(86400000L, "day", "d"),
+ WEEK(604800000L, "week", "w"),
+ MONTH(2592000000L, "Month", "M");
+
+ /** Count of milliseconds */
+ private final long _millis;
+ /** Mnemonic name of period */
+ private final String _name;
+ /** Short name of period */
+ private final String _shortName;
+
+ private TimeConstant(long millis, String name, String shortName)
+ {
+ _millis = millis;
+ _name = name;
+ _shortName = shortName;
+ }
+
+ /**
+ * @return number of millisecond in time period
+ */
+ public long getTimeInMillis()
+ {
+ return _millis;
+ }
+
+ /**
+ * @return mnemonic name of time period
+ */
+ public String getName()
+ {
+ return _name;
+ }
+
+ /**
+ * @return short name of time period
+ */
+ public String getShortName()
+ {
+ return _shortName;
+ }
+}
\ No newline at end of file
Index: java/com/l2jserver/gameserver/util/Util.java
===================================================================
--- java/com/l2jserver/gameserver/util/Util.java (revision 6303)
+++ java/com/l2jserver/gameserver/util/Util.java (working copy)
@@ -24,6 +24,7 @@
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -491,6 +492,100 @@
return dateFormat.format(date.getTime());
}
+ /**
+ * @param firstDate first date to check
+ * @param secondDate second date to check
+ * @return {@code true} if both given dates is same day
+ */
+ public static boolean isSameDay(Date firstDate, Date secondDate)
+ {
+ Calendar first = Calendar.getInstance();
+ Calendar second = Calendar.getInstance();
+ first.setTime(firstDate);
+ second.setTime(secondDate);
+
+ return (first.get(Calendar.ERA) == second.get(Calendar.ERA) &&
+ first.get(Calendar.YEAR) == second.get(Calendar.YEAR) &&
+ first.get(Calendar.DAY_OF_YEAR) == second.get(Calendar.DAY_OF_YEAR));
+ }
+
+ /**
+ * @param date date to check
+ * @return {@code true} if given date is tomorrow
+ */
+ public static boolean isToday(Date date)
+ {
+ Calendar now = Calendar.getInstance();
+ Calendar test = Calendar.getInstance();
+ test.setTime(date);
+
+ return isSameDay(now.getTime(), test.getTime());
+ }
+
+ /**
+ * @param date date to check
+ * @return {@code true} if given date is today
+ */
+ public static boolean isTomorrow(Date date)
+ {
+ Calendar now = Calendar.getInstance();
+ now.add(Calendar.DAY_OF_YEAR, 1);
+ Calendar test= Calendar.getInstance();
+ test.setTime(date);
+
+ return isSameDay(now.getTime(), test.getTime());
+ }
+
+ /**
+ * @param defStr string to parse. Format is "<number><supported tag>", supported tags are "s" for seconds, "m" for minutes,
+ * "h" for hours, "d" for days, "w" for weeks, m - for conventional "month" (30 days). Valid value for example: "25s"
+ * @return number of milliseconds in given period, or -1 if format of period is not valid
+ */
+ public static long toMillis(String defStr)
+ {
+ if (defStr == null)
+ {
+ return -1;
+ }
+
+ long ret = -1;
+ String toNum = defStr.substring(0, defStr.length() - 1); // Whole string, except last symbol
+ String period = defStr.substring(defStr.length() - 1, defStr.length()); // assume, that last symbol is code of time period
+
+ TimeConstant tc = getTimeConstant(period);
+ if (tc != TimeConstant.NONE)
+ {
+ try
+ {
+ int num = Integer.parseInt(toNum);
+ ret = tc.getTimeInMillis() * num;
+ }
+ catch(NumberFormatException nfe)
+ {
+ // Do nothing
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * @param defStr supported tag to parse.Supported tags are "s" for seconds, "m" for minutes,
+ * "h" for hours, "d" for days, "w" for weeks, M - for conventional "month" (30 days).
+ * @return TimeConstant object, corresponding to given tag, or TimeConstant.NONE for invalid tags.
+ */
+ public static TimeConstant getTimeConstant(String defStr)
+ {
+ for (TimeConstant tc : TimeConstant.values())
+ {
+ if (tc.getShortName().equals(defStr))
+ {
+ return tc;
+ }
+ }
+ return TimeConstant.NONE;
+ }
+
private static final void buildHtmlBypassCache(L2PcInstance player, HtmlActionScope scope, String html)
{
String htmlLower = html.toLowerCase(Locale.ENGLISH);
Index: dist/game/config/adminCommands.xml
===================================================================
--- dist/game/config/adminCommands.xml (revision 10070)
+++ dist/game/config/adminCommands.xml (working copy)
@@ -442,7 +442,13 @@
<admin command="admin_untransform" accessLevel="7" />
<admin command="admin_transform_menu" accessLevel="7" />
<admin command="admin_untransform_menu" accessLevel="7" />
-
+
+ <!-- ADMIN PREMIUM SERVICE -->
+ <admin command="admin_add_premium" accessLevel="7" confirmDlg="true" />
+ <admin command="admin_give_acc_premium" accessLevel="7" confirmDlg="true" />
+ <admin command="admin_give_char_premium" accessLevel="7" confirmDlg="true" />
+ <admin command="admin_remove_premium" accessLevel="7" confirmDlg="true" />
+
<!-- ADMIN QUEST -->
<admin command="admin_quest_reload" accessLevel="7" />
<admin command="admin_script_load" accessLevel="7" />
Index: dist/game/data/html/admin/charinfo.htm
===================================================================
--- dist/game/data/html/admin/charinfo.htm (revision 10070)
+++ dist/game/data/html/admin/charinfo.htm (working copy)
@@ -25,6 +25,9 @@
<td><button value="Effects" action="bypass -h admin_getbuffs %name%" width=65 height=18 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td>
<td><button value="Party" action="bypass -h admin_partyinfo" width=65 height=18 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td>
</tr>
+<tr>
+<td><center><button value="Premium" action="bypass -h admin_html editprem.htm" width=65 height=18 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></center></td>
+</tr>
</table>
<br>
<table width=240 bgcolor="666666">
@@ -77,6 +80,9 @@
<tr>
<td>Noblesse:</td><td>%noblesse%</td>
</tr>
+<tr>
+<td>Premium:</td><td>%premium%</td>
+</tr>
%inst%
</table>
<br>
Index: dist/game/data/html/admin/editprem.htm
===================================================================
--- dist/game/data/html/admin/editprem.htm (revision 0)
+++ dist/game/data/html/admin/editprem.htm (working copy)
@@ -0,0 +1,16 @@
+<head><body>
+<table width=260><tr>
+<td width=40><button value="Main" action="bypass -h admin_admin" width=40 height=21 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td>
+<td width=180><center>Premium Account Admin Menu</center></td>
+<td width=40><button value="Back" action="bypass -h admin_character_info %name%" width=40 height=21 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td>
+</tr></table>
+<br>
+<table width=270>
+<tr><td width=140>Premium Account:</td><td><edit width=80 var="premacc"></td><td><button value="Set" action="bypass -h admin_give_acc_premium $premacc" height=21 width=45 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td></tr>
+<tr><td width=140>Premium Character:</td><td><edit width=80 var="premchar"></td><td><button value="Set" action="bypass -h admin_give_char_premium $premchar" height=21 width=45 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td></tr>
+<tr><td width=140>Premium Extend:</td><td><edit width=80 var="extprem"></td><td><button value="Save" action="bypass -h admin_add_premium $extprem" height=21 width=45 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"></td></tr>
+<tr></tr>
+</table>
+<br>
+</body>
+</html>
\ No newline at end of file
Index: dist/game/data/html/mods/services/list.html
===================================================================
--- dist/game/data/html/mods/services/list.html (revision 0)
+++ dist/game/data/html/mods/services/list.html (working copy)
@@ -0,0 +1,5 @@
+<html><body>
+ <center>
+ <button value="Premium Service" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/misconfigureg.html
===================================================================
--- dist/game/data/html/mods/services/misconfigureg.html (revision 0)
+++ dist/game/data/html/mods/services/misconfigureg.html (working copy)
@@ -0,0 +1,3 @@
+<html><body>
+ Service isn't configured properly. Please, report to Game Master.
+</body></html>
Index: dist/game/data/html/mods/services/not_allowed.html
===================================================================
--- dist/game/data/html/mods/services/not_allowed.html (revision 0)
+++ dist/game/data/html/mods/services/not_allowed.html (working copy)
@@ -0,0 +1,3 @@
+<html><body>
+ This action is not allowed.
+</body></html>
\ No newline at end of file
Index: dist/game/data/html/mods/services/premium/account.html
===================================================================
--- dist/game/data/html/mods/services/premium/account.html (revision 0)
+++ dist/game/data/html/mods/services/premium/account.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ <center>
+ <button value="1 Hour - 1 adena" action="bypass -h premiumAddPremium account 1h" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Day - 24 adena" action="bypass -h premiumAddPremium account 1d" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Month - 720 adena" action="bypass -h premiumAddPremium account 1M" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/char.html
===================================================================
--- dist/game/data/html/mods/services/premium/char.html (revision 0)
+++ dist/game/data/html/mods/services/premium/char.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ <center>
+ <button value="1 Hour - 1 adena" action="bypass -h premiumAddPremium char 1h" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Day - 24 adena" action="bypass -h premiumAddPremium char 1d" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Month - 720 adena" action="bypass -h premiumAddPremium char 1M" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/choice.html
===================================================================
--- dist/game/data/html/mods/services/premium/choice.html (revision 0)
+++ dist/game/data/html/mods/services/premium/choice.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ Current status: %status%<br>
+ <center>
+ <button value="Account Premium" action="bypass -h premiumShowList account" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="Character Premium" action="bypass -h premiumShowList char" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/service_disabled.html
===================================================================
--- dist/game/data/html/mods/services/service_disabled.html (revision 0)
+++ dist/game/data/html/mods/services/service_disabled.html (working copy)
@@ -0,0 +1,3 @@
+<html><body>
+ Service is disabled.
+</body></html>
Index: dist/game/data/html/mods/services/list.html
===================================================================
--- dist/game/data/html/mods/services/list.html (revision 0)
+++ dist/game/data/html/mods/services/list.html (working copy)
@@ -0,0 +1,5 @@
+<html><body>
+ <center>
+ <button value="Premium Service" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/misconfigureg.html
===================================================================
--- dist/game/data/html/mods/services/misconfigureg.html (revision 0)
+++ dist/game/data/html/mods/services/misconfigureg.html (working copy)
@@ -0,0 +1,3 @@
+<html><body>
+ Service isn't configured properly. Please, report to Game Master.
+</body></html>
Index: dist/game/data/html/mods/services/not_allowed.html
===================================================================
--- dist/game/data/html/mods/services/not_allowed.html (revision 0)
+++ dist/game/data/html/mods/services/not_allowed.html (working copy)
@@ -0,0 +1,3 @@
+<html><body>
+ This action is not allowed.
+</body></html>
\ No newline at end of file
Index: dist/game/data/html/mods/services/premium/account.html
===================================================================
--- dist/game/data/html/mods/services/premium/account.html (revision 0)
+++ dist/game/data/html/mods/services/premium/account.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ <center>
+ <button value="1 Hour - 1 adena" action="bypass -h premiumAddPremium account 1h" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Day - 24 adena" action="bypass -h premiumAddPremium account 1d" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Month - 720 adena" action="bypass -h premiumAddPremium account 1M" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/char.html
===================================================================
--- dist/game/data/html/mods/services/premium/char.html (revision 0)
+++ dist/game/data/html/mods/services/premium/char.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ <center>
+ <button value="1 Hour - 1 adena" action="bypass -h premiumAddPremium char 1h" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Day - 24 adena" action="bypass -h premiumAddPremium char 1d" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Month - 720 adena" action="bypass -h premiumAddPremium char 1M" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/choice.html
===================================================================
--- dist/game/data/html/mods/services/premium/choice.html (revision 0)
+++ dist/game/data/html/mods/services/premium/choice.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ Current status: %status%<br>
+ <center>
+ <button value="Account Premium" action="bypass -h premiumShowList account" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="Character Premium" action="bypass -h premiumShowList char" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/account.html
===================================================================
--- dist/game/data/html/mods/services/premium/account.html (revision 0)
+++ dist/game/data/html/mods/services/premium/account.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ <center>
+ <button value="1 Hour - 1 adena" action="bypass -h premiumAddPremium account 1h" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Day - 24 adena" action="bypass -h premiumAddPremium account 1d" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Month - 720 adena" action="bypass -h premiumAddPremium account 1M" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/char.html
===================================================================
--- dist/game/data/html/mods/services/premium/char.html (revision 0)
+++ dist/game/data/html/mods/services/premium/char.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ <center>
+ <button value="1 Hour - 1 adena" action="bypass -h premiumAddPremium char 1h" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Day - 24 adena" action="bypass -h premiumAddPremium char 1d" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="1 Month - 720 adena" action="bypass -h premiumAddPremium char 1M" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/premium/choice.html
===================================================================
--- dist/game/data/html/mods/services/premium/choice.html (revision 0)
+++ dist/game/data/html/mods/services/premium/choice.html (working copy)
@@ -0,0 +1,8 @@
+<html><body>
+ Current status: %status%<br>
+ <center>
+ <button value="Account Premium" action="bypass -h premiumShowList account" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ <button value="Character Premium" action="bypass -h premiumShowList char" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF"><br>
+ <button value="Back" action="bypass -h premiumShowList" width=220 height=30 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF">
+ </center>
+</body></html>
Index: dist/game/data/html/mods/services/service_disabled.html
===================================================================
--- dist/game/data/html/mods/services/service_disabled.html (revision 0)
+++ dist/game/data/html/mods/services/service_disabled.html (working copy)
@@ -0,0 +1,3 @@
+<html><body>
+ Service is disabled.
+</body></html>
Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminEditChar.java
===================================================================
--- dist/game/data/scripts/handlers/admincommandhandlers/AdminEditChar.java (revision 10070)
+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminEditChar.java (working copy)
@@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,6 +37,8 @@
import com.l2jserver.gameserver.datatables.CharNameTable;
import com.l2jserver.gameserver.datatables.ClassListData;
import com.l2jserver.gameserver.handler.IAdminCommandHandler;
+import com.l2jserver.gameserver.instancemanager.ExpirableServicesManager;
+import com.l2jserver.gameserver.enums.ServiceType;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.L2Character;
@@ -1045,6 +1048,27 @@
adminReply.replace("%ai%", String.valueOf(player.getAI().getIntention().name()));
adminReply.replace("%inst%", player.getInstanceId() > 0 ? "<tr><td>InstanceId:</td><td><a action=\"bypass -h admin_instance_spawns " + String.valueOf(player.getInstanceId()) + "\">" + String.valueOf(player.getInstanceId()) + "</a></td></tr>" : "");
adminReply.replace("%noblesse%", player.isNoble() ? "Yes" : "No");
+ String premiumInfo;
+ if (player.hasPremium())
+ {
+ if (ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).isUnlimited())
+ {
+ premiumInfo = "Active, unlimited time";
+ }
+ else if (ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).isTemporary())
+ {
+ premiumInfo = "Active until logout";
+ }
+ else
+ {
+ premiumInfo = "Active until " + Util.formatDate(new Date(ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, player).getExpirationDate()), "dd.MM.yyyy HH:mm");
+ }
+ }
+ else
+ {
+ premiumInfo = "None";
+ }
+ adminReply.replace("%premium%", premiumInfo);
activeChar.sendPacket(adminReply);
}
Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPremium.java
===================================================================
--- dist/game/data/scripts/handlers/admincommandhandlers/AdminPremium.java (revision 0)
+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPremium.java (working copy)
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2004-2013 L2J DataPack
+ *
+ * This file is part of L2J DataPack.
+ *
+ * L2J DataPack 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.
+ *
+ * L2J DataPack 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 handlers.admincommandhandlers;
+
+import com.l2jserver.gameserver.handler.IAdminCommandHandler;
+import com.l2jserver.gameserver.datatables.PremiumTable;
+import com.l2jserver.gameserver.model.L2Object;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.SystemMessageId;
+import com.l2jserver.gameserver.util.Util;
+
+import java.util.StringTokenizer;
+
+/**
+ * @author GKR
+ */
+public class AdminPremium implements IAdminCommandHandler
+{
+ private static final String[] ADMIN_COMMANDS =
+ {
+ "admin_add_premium",
+ "admin_give_acc_premium",
+ "admin_give_char_premium",
+ "admin_remove_premium"
+ };
+
+ @Override
+ public boolean useAdminCommand(String command, L2PcInstance activeChar)
+ {
+ final StringTokenizer st = new StringTokenizer(command, " ");
+ final String cmd = st.nextToken();
+ L2PcInstance player = null;
+ final L2Object targetObj = activeChar.getTarget();
+ boolean success = true;
+
+ if ((targetObj != null) && targetObj.isPlayer())
+ {
+ player = targetObj.getActingPlayer();
+ }
+ else
+ {
+ activeChar.sendPacket(SystemMessageId.INCORRECT_TARGET);
+ return false;
+ }
+
+ if (cmd.equals("admin_add_premium") || cmd.equals("admin_give_acc_premium") || cmd.equals("admin_give_char_premium"))
+ {
+ final String val = st.hasMoreTokens() ? st.nextToken() : null;
+ final long millis = Util.toMillis(val);
+
+ if (cmd.equals("admin_add_premium"))
+ {
+ if (val == null)
+ {
+ activeChar.sendMessage("Invalid syntax. Usage: add_premium number{s|m|h|d|M} (e.g. 1d)");
+ }
+ else if (millis < 0)
+ {
+ activeChar.sendMessage("Invalid time umit. Usage: add_premium number{s|m|h|d|M} (e.g. 1d)");
+ }
+ else if (!activeChar.hasPremium())
+ {
+ activeChar.sendMessage(player.getName() + " hasn't active premium service, cannot add time");
+ }
+ else
+ {
+ success = PremiumTable.addTime(player, millis);
+ if (success)
+ {
+ activeChar.sendMessage("Premium Service for " + player.getName() + ": time was added");
+ }
+ }
+ }
+ else if (cmd.equals("admin_give_acc_premium") || cmd.equals("admin_give_char_premium"))
+ {
+ // No time is specified - unlimited service
+ if (val == null)
+ {
+ success = PremiumTable.setUnlimitedPremium(player, cmd.equals("admin_give_acc_premium"));
+ if (success)
+ {
+ activeChar.sendMessage("Unlimited Premium Service for " + player.getName() + " was activated");
+ }
+ }
+ // Wrong time is specified
+ else if (millis < 0)
+ {
+ activeChar.sendMessage("Invalid time umit. Usage: give_acc_premium|give_char_premium number{s|m|h|d|M} (e.g. 1d)");
+ }
+ // Disallow to extend, if already has premium service
+ else if (activeChar.hasPremium())
+ {
+ activeChar.sendMessage(player.getName() + " has active premium service. Use add_premium instead");
+ }
+ else
+ {
+ success = PremiumTable.addTime(player, millis, cmd.equals("admin_give_acc_premium"));
+ if (success)
+ {
+ activeChar.sendMessage("Premium Service for " + player.getName() + " was activated");
+ }
+ }
+ }
+
+ if (!success)
+ {
+ activeChar.sendMessage("No action. Something went wrong");
+ }
+ }
+
+ return success;
+ }
+
+ @Override
+ public String[] getAdminCommandList()
+ {
+ return ADMIN_COMMANDS;
+ }
+}
\ No newline at end of file
Index: dist/game/data/scripts/handlers/bypasshandlers/Premium.java
===================================================================
--- dist/game/data/scripts/handlers/bypasshandlers/Premium.java (revision 0)
+++ dist/game/data/scripts/handlers/bypasshandlers/Premium.java (working copy)
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2004-2013 L2J DataPack
+ *
+ * This file is part of L2J DataPack.
+ *
+ * L2J DataPack 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.
+ *
+ * L2J DataPack 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 handlers.bypasshandlers;
+
+import java.util.Date;
+import java.util.StringTokenizer;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.cache.HtmCache;
+import com.l2jserver.gameserver.datatables.PremiumTable;
+import com.l2jserver.gameserver.enums.ServiceType;
+import com.l2jserver.gameserver.handler.IBypassHandler;
+import com.l2jserver.gameserver.instancemanager.ExpirableServicesManager;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
+import com.l2jserver.gameserver.util.Util;
+
+/**
+ * This class provides handling of premium service bypasses
+ * @author GKR
+ */
+
+public class Premium implements IBypassHandler
+{
+ private static final String[] COMMANDS =
+ {
+ "premiumShowList",
+ "premiumAddPremium",
+ };
+
+ @Override
+ public boolean useBypass(String command, L2PcInstance activeChar, L2Character target)
+ {
+ try
+ {
+ String path = "data/html/mods/services/";
+ String reply = null;
+ String htmContent = null;
+
+ if (!Config.PREMIUM_SERVICE_ENABLED)
+ {
+ reply = "service_disabled";
+ }
+
+ else
+ {
+ if (command.startsWith("premiumShowList"))
+ {
+ if (command.equals("premiumShowList"))
+ {
+ reply = "premium/choice";
+ }
+ else
+ {
+ switch (command.substring(16))
+ {
+ case "account":
+ case "char":
+ {
+ reply = "premium/" + command.substring(16);
+ break;
+ }
+ }
+ }
+ }
+ else if (command.startsWith("premiumAddPremium"))
+ {
+ StringTokenizer st = new StringTokenizer(command, " ");
+ st.nextToken();
+
+ // check if both mode and time are passed
+ if (st.countTokens() < 2)
+ {
+ _log.warning(getClass().getSimpleName() + ": wrong bypass from player " + activeChar.getName());
+ return false;
+ }
+
+ String mode = st.nextToken();
+ String period = st.nextToken();
+
+ // No need to check anything - all checks are in givePremium() method
+ switch (mode)
+ {
+ case "account":
+ {
+ PremiumTable.giveAccountPremium(activeChar, period, null);
+ break;
+ }
+ case "char":
+ {
+ PremiumTable.giveCharPremium(activeChar, period, null);
+ break;
+ }
+ default:
+ {
+ _log.warning(getClass().getSimpleName() + ": wrong bypass from player " + activeChar.getName());
+ }
+ }
+ }
+ }
+ // Check for html reply
+ if (reply != null)
+ {
+ htmContent = HtmCache.getInstance().getHtm(activeChar.getHtmlPrefix(), path + reply + ".html");
+ if (htmContent != null)
+ {
+ NpcHtmlMessage html = new NpcHtmlMessage(0, htmContent);
+
+ // Print current premium status
+ if (command.equals("premiumShowList"))
+ {
+ String premiumInfo;
+ if (activeChar.hasPremium())
+ {
+ if (ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, activeChar).isUnlimited())
+ {
+ premiumInfo = "Active, unlimited time";
+ }
+ else if (ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, activeChar).isTemporary())
+ {
+ premiumInfo = "Active until logout";
+ }
+ else
+ {
+ premiumInfo = "Active until " + Util.formatDate(new Date(ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, activeChar).getExpirationDate()), "dd.MM.yyyy HH:mm");
+ }
+
+ premiumInfo = (ExpirableServicesManager.getInstance().getService(ServiceType.PREMIUM, activeChar).isAccountBased() ? "Account type, " : "Char type, ") + premiumInfo;
+ }
+ else
+ {
+ premiumInfo = "None";
+ }
+ html.replace("%status%", premiumInfo);
+ }
+
+ activeChar.sendPacket(html);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _log.warning("Exception in " + getClass().getSimpleName() + ": " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ @Override
+ public String[] getBypassList()
+ {
+ return COMMANDS;
+ }
+}
\ No newline at end of file
Index: dist/game/data/scripts/handlers/MasterHandler.java
===================================================================
--- dist/game/data/scripts/handlers/MasterHandler.java (revision 10070)
+++ dist/game/data/scripts/handlers/MasterHandler.java (working copy)
@@ -103,6 +103,7 @@
import handlers.admincommandhandlers.AdminPetition;
import handlers.admincommandhandlers.AdminPledge;
import handlers.admincommandhandlers.AdminPolymorph;
+import handlers.admincommandhandlers.AdminPremium;
import handlers.admincommandhandlers.AdminPunishment;
import handlers.admincommandhandlers.AdminQuest;
import handlers.admincommandhandlers.AdminReload;
@@ -143,6 +144,7 @@
import handlers.bypasshandlers.OlympiadManagerLink;
import handlers.bypasshandlers.OlympiadObservation;
import handlers.bypasshandlers.PlayerHelp;
+import handlers.bypasshandlers.Premium;
import handlers.bypasshandlers.PrivateWarehouse;
import handlers.bypasshandlers.QuestLink;
import handlers.bypasshandlers.QuestList;
@@ -275,6 +277,7 @@
import handlers.voicedcommandhandlers.Debug;
import handlers.voicedcommandhandlers.Hellbound;
import handlers.voicedcommandhandlers.Lang;
+import handlers.voicedcommandhandlers.Service;
import handlers.voicedcommandhandlers.StatsVCmd;
import handlers.voicedcommandhandlers.TvTVoicedInfo;
import handlers.voicedcommandhandlers.Wedding;
@@ -380,6 +383,7 @@
AdminPForge.class,
AdminPledge.class,
AdminPolymorph.class,
+ AdminPremium.class,
AdminPunishment.class,
AdminQuest.class,
AdminReload.class,
@@ -422,6 +426,7 @@
Observation.class,
OlympiadObservation.class,
OlympiadManagerLink.class,
+ Premium.class,
QuestLink.class,
PlayerHelp.class,
PrivateWarehouse.class,
@@ -532,6 +537,7 @@
// TODO: Add configuration options for this voiced commands:
// CastleVCmd.class,
// SetVCmd.class,
+ (Config.ALLOW_SERVICE_VOICED ? Service.class : null),
(Config.L2JMOD_ALLOW_WEDDING ? Wedding.class : null),
(Config.BANKING_SYSTEM_ENABLED ? Banking.class : null),
(Config.TVT_ALLOW_VOICED_COMMAND ? TvTVoicedInfo.class : null),
Index: dist/game/data/scripts/handlers/voicedcommandhandlers/Service.java
===================================================================
--- dist/game/data/scripts/handlers/voicedcommandhandlers/Service.java (revision 0)
+++ dist/game/data/scripts/handlers/voicedcommandhandlers/Service.java (working copy)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004-2013 L2J DataPack
+ *
+ * This file is part of L2J DataPack.
+ *
+ * L2J DataPack 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.
+ *
+ * L2J DataPack 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 handlers.voicedcommandhandlers;
+
+import com.l2jserver.gameserver.cache.HtmCache;
+import com.l2jserver.gameserver.handler.IVoicedCommandHandler;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
+
+public class Service implements IVoicedCommandHandler
+{
+ private static final String[] VOICED_COMMANDS =
+ {
+ "service"
+ };
+
+ @Override
+ public boolean useVoicedCommand(String command, L2PcInstance activeChar, String target)
+ {
+ final NpcHtmlMessage msg = new NpcHtmlMessage(0, HtmCache.getInstance().getHtmForce(activeChar.getHtmlPrefix(), "data/html/mods/services/list.html"));
+ activeChar.sendPacket(msg);
+ return true;
+ }
+
+ @Override
+ public String[] getVoicedCommandList()
+ {
+ return VOICED_COMMANDS;
+ }
+}
\ No newline at end of file
Index: dist/sql/game/account_services.sql
===================================================================
--- dist/sql/game/account_services.sql (revision 0)
+++ dist/sql/game/account_services.sql (working copy)
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS `account_services` (
+ `charLogin` VARCHAR(45) NOT NULL,
+ `serviceName` varchar(50) NOT NULL,
+ `expiration` bigint(13) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`charLogin`,`serviceName`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Index: dist/sql/game/character_services.sql
===================================================================
--- dist/sql/game/character_services.sql (revision 0)
+++ dist/sql/game/character_services.sql (working copy)
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS `character_services` (
+ `charId` int(10) unsigned NOT NULL,
+ `serviceName` varchar(50) NOT NULL,
+ `expiration` bigint(13) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`charId`,`serviceName`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment