Răsfoiți Sursa

BETA: SSQ Missing feature Lord of the Manor's Certificate of Approval:
* Implemented method isMyLord(player), verify if the player is clan leader and own the NPC's assigned castle or fort.
* Added configuration for some parameters and unhardcoded other related to SSQ.
* Support for sold tickets reset after castle's owner change or SSQ event change.
* Refactored some old SSQ code.
* Removed bsh-engine.txt file.
* Fixed few JavaDocs.

Reported by: valdaron

'''Note:''' C3 feature.

Zoey76 12 ani în urmă
părinte
comite
3414b8dbcb

+ 0 - 32
L2J_Server_BETA/dist/doc/3rdPartyLicenses/bsh-engine.txt

@@ -1,32 +0,0 @@
-Copyright (c) 2006, Sun Microsystems, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without 
-modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice, this 
-   list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above copyright notice, 
-   this list of conditions and the following disclaimer in the documentation 
-   and/or other materials provided with the distribution.
-
- - Neither the name of the Sun Microsystems, Inc. nor the names of 
-   contributors may be used to endorse or promote products derived from this 
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
-CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 
-USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
-DAMAGE.

+ 17 - 0
L2J_Server_BETA/dist/game/config/Feature.properties

@@ -271,6 +271,23 @@ StrictSevenSigns = True
 # Default: True
 AltSevenSignsLazyUpdate = True
 
+# Total count of available tickets.
+# Default: 300
+SevenSignsDawnTicketQuantity = 300
+
+# Price of each ticket.
+# Default: 1000
+SevenSignsDawnTicketPrice = 1000
+
+# Tickets bundle (exchanged in amounts of).
+# Default: 10
+SevenSignsDawnTicketBundle = 10
+
+# Ticket item Id.
+# Default: 6388
+SevenSignsManorsAgreementId = 6388
+
+
 # ---------------------------------------------------------------------------
 # Clan Reputation Points
 # ---------------------------------------------------------------------------

+ 11 - 0
L2J_Server_BETA/java/com/l2jserver/Config.java

@@ -907,6 +907,11 @@ public final class Config
 	public static double ALT_SIEGE_DUSK_GATES_MDEF_MULT;
 	public static boolean ALT_STRICT_SEVENSIGNS;
 	public static boolean ALT_SEVENSIGNS_LAZY_UPDATE;
+	public static int SSQ_DAWN_TICKET_QUANTITY;
+	public static int SSQ_DAWN_TICKET_PRICE;
+	public static int SSQ_DAWN_TICKET_BUNDLE;
+	public static int SSQ_MANORS_AGREEMENT_ID;
+	public static int SSQ_JOIN_DAWN_ADENA_FEE;
 	
 	// --------------------------------------------------
 	// Server Settings
@@ -1379,6 +1384,12 @@ public final class Config
 			ALT_STRICT_SEVENSIGNS = Boolean.parseBoolean(Feature.getProperty("StrictSevenSigns", "True"));
 			ALT_SEVENSIGNS_LAZY_UPDATE = Boolean.parseBoolean(Feature.getProperty("AltSevenSignsLazyUpdate", "True"));
 			
+			SSQ_DAWN_TICKET_QUANTITY = Integer.parseInt(Feature.getProperty("SevenSignsDawnTicketQuantity", "300"));
+			SSQ_DAWN_TICKET_PRICE = Integer.parseInt(Feature.getProperty("SevenSignsDawnTicketPrice", "1000"));
+			SSQ_DAWN_TICKET_BUNDLE = Integer.parseInt(Feature.getProperty("SevenSignsDawnTicketBundle", "10"));
+			SSQ_MANORS_AGREEMENT_ID = Integer.parseInt(Feature.getProperty("SevenSignsManorsAgreementId", "6388"));
+			SSQ_JOIN_DAWN_ADENA_FEE = Integer.parseInt(Feature.getProperty("SevenSignsJoinDawnFee", "50000"));
+			
 			TAKE_FORT_POINTS = Integer.parseInt(Feature.getProperty("TakeFortPoints", "200"));
 			LOOSE_FORT_POINTS = Integer.parseInt(Feature.getProperty("LooseFortPoints", "0"));
 			TAKE_CASTLE_POINTS = Integer.parseInt(Feature.getProperty("TakeCastlePoints", "1500"));

+ 22 - 18
L2J_Server_BETA/java/com/l2jserver/gameserver/SevenSigns.java

@@ -22,6 +22,7 @@ import java.sql.Statement;
 import java.util.Calendar;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -84,9 +85,7 @@ public class SevenSigns
 	public static final int PERIOD_MAJOR_LENGTH = 604800000 - PERIOD_MINOR_LENGTH;
 	
 	public static final int RECORD_SEVEN_SIGNS_ID = 5707;
-	public static final int CERTIFICATE_OF_APPROVAL_ID = 6388;
 	public static final int RECORD_SEVEN_SIGNS_COST = 500;
-	public static final int ADENA_JOIN_DAWN_COST = 50000;
 	
 	// NPC Related Constants \\
 	public static final int ORATOR_NPC_ID = 31094;
@@ -515,21 +514,16 @@ public class SevenSigns
 	private final int getDaysToPeriodChange()
 	{
 		int numDays = _nextPeriodChange.get(Calendar.DAY_OF_WEEK) - PERIOD_START_DAY;
-		
 		if (numDays < 0)
 		{
 			return 0 - numDays;
 		}
-		
 		return 7 - numDays;
 	}
 	
 	public final long getMilliToPeriodChange()
 	{
-		long currTimeMillis = System.currentTimeMillis();
-		long changeTimeMillis = _nextPeriodChange.getTimeInMillis();
-		
-		return (changeTimeMillis - currTimeMillis);
+		return (_nextPeriodChange.getTimeInMillis() - System.currentTimeMillis());
 	}
 	
 	protected void setCalendarForNextPeriodChange()
@@ -590,10 +584,17 @@ public class SevenSigns
 				periodName = "Seal Validation";
 				break;
 		}
-		
 		return periodName;
 	}
 	
+	/**
+	 * @return {@code true} if it's competition period, {@code false} otherwise
+	 */
+	public final boolean isCompetitionPeriod()
+	{
+		return (_activePeriod == PERIOD_COMPETITION);
+	}
+	
 	public final boolean isSealValidationPeriod()
 	{
 		return (_activePeriod == PERIOD_SEAL_VALIDATION);
@@ -1213,8 +1214,7 @@ public class SevenSigns
 	 */
 	public void sendMessageToAll(SystemMessageId sysMsgId)
 	{
-		SystemMessage sm = SystemMessage.getSystemMessage(sysMsgId);
-		Broadcast.toAllOnlinePlayers(sm);
+		Broadcast.toAllOnlinePlayers(SystemMessage.getSystemMessage(sysMsgId));
 	}
 	
 	/**
@@ -1224,24 +1224,22 @@ public class SevenSigns
 	 */
 	protected void initializeSeals()
 	{
-		for (Integer currSeal : _signsSealOwners.keySet())
+		for (Entry<Integer, Integer> e : _signsSealOwners.entrySet())
 		{
-			int sealOwner = _signsSealOwners.get(currSeal);
-			
-			if (sealOwner != CABAL_NULL)
+			if (e.getValue() != CABAL_NULL)
 			{
 				if (isSealValidationPeriod())
 				{
-					_log.info("SevenSigns: The " + getCabalName(sealOwner) + " have won the " + getSealName(currSeal, false) + ".");
+					_log.info("SevenSigns: The " + getCabalName(e.getValue()) + " have won the " + getSealName(e.getKey(), false) + ".");
 				}
 				else
 				{
-					_log.info("SevenSigns: The " + getSealName(currSeal, false) + " is currently owned by " + getCabalName(sealOwner) + ".");
+					_log.info("SevenSigns: The " + getSealName(e.getKey(), false) + " is currently owned by " + getCabalName(e.getValue()) + ".");
 				}
 			}
 			else
 			{
-				_log.info("SevenSigns: The " + getSealName(currSeal, false) + " remains unclaimed.");
+				_log.info("SevenSigns: The " + getSealName(e.getKey(), false) + " remains unclaimed.");
 			}
 		}
 	}
@@ -1532,6 +1530,12 @@ public class SevenSigns
 					}
 					
 					_previousWinner = compWinner;
+					// Reset Castle ticket buy count
+					List<Castle> castles = CastleManager.getInstance().getCastles();
+					for (Castle castle : castles)
+					{
+						castle.setTicketBuyCount(0);
+					}
 					break;
 				case PERIOD_COMP_RESULTS: // Seal Validation
 					

+ 16 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Npc.java

@@ -829,6 +829,22 @@ public class L2Npc extends L2Character
 		return CastleManager.getInstance().getCastles().get(_castleIndex);
 	}
 	
+	/**
+	 * Verify if the given player is this NPC's lord.
+	 * @param player the player to check
+	 * @return {@code true} if the player is clan leader and owner of a castle of fort that this NPC belongs to, {@code false} otherwise
+	 */
+	public boolean isMyLord(L2PcInstance player)
+	{
+		if (player.isClanLeader())
+		{
+			final int castleId = getCastle() != null ? getCastle().getCastleId() : -1;
+			final int fortId = getFort() != null ? getFort().getFortId() : -1;
+			return (player.getClan().getCastleId() == castleId) || (player.getClan().getFortId() == fortId);
+		}
+		return false;
+	}
+	
 	public final SiegableHall getConquerableHall()
 	{
 		return CHSiegeManager.getInstance().getNearbyClanHall(getX(), getY(), 10000);

+ 100 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2CastleChamberlainInstance.java

@@ -42,8 +42,13 @@ import com.l2jserver.gameserver.util.Util;
 import com.l2jserver.util.StringUtil;
 
 /**
- * Castle Chamberlains implementation used for: - tax rate control - regional<br>
- * manor system control - castle treasure control - ...
+ * Castle Chamberlains implementation used for:
+ * <ul>
+ * <li>Tax rate control regional</li>
+ * <li>Manor system control</li>
+ * <li>Castle treasure control</li>
+ * <li>Manor's agreement exchange</li>
+ * </ul>
  */
 public class L2CastleChamberlainInstance extends L2MerchantInstance
 {
@@ -91,7 +96,7 @@ public class L2CastleChamberlainInstance extends L2MerchantInstance
 			String actualCommand = st.nextToken(); // Get actual command
 			
 			String val = "";
-			if (st.countTokens() >= 1)
+			if (st.hasMoreTokens())
 			{
 				val = st.nextToken();
 			}
@@ -1244,6 +1249,98 @@ public class L2CastleChamberlainInstance extends L2MerchantInstance
 				player.sendPacket(html);
 				return;
 			}
+			else if (actualCommand.equalsIgnoreCase("manors_cert"))
+			{
+				final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+				// Zoey76: TODO: Check privileges and replace with enum/constants.
+				if (isMyLord(player) || (validatePrivileges(player, 5) && (validateCondition(player) == COND_OWNER)))
+				{
+					if (getCastle().getSiege().getIsInProgress())
+					{
+						html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-busy.htm");
+						html.replace("%npcname%", String.valueOf(getName()));
+					}
+					else
+					{
+						final int cabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId());
+						if ((cabal == SevenSigns.CABAL_DAWN) && SevenSigns.getInstance().isCompetitionPeriod())
+						{
+							final int ticketCount = getCastle().getTicketBuyCount();
+							if (ticketCount < (Config.SSQ_DAWN_TICKET_QUANTITY / Config.SSQ_DAWN_TICKET_BUNDLE))
+							{
+								html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/ssq_selldawnticket.htm");
+								html.replace("%DawnTicketLeft%", String.valueOf(Config.SSQ_DAWN_TICKET_QUANTITY - (ticketCount * Config.SSQ_DAWN_TICKET_BUNDLE)));
+								html.replace("%DawnTicketBundle%", String.valueOf(Config.SSQ_DAWN_TICKET_BUNDLE));
+								html.replace("%DawnTicketPrice%", String.valueOf(Config.SSQ_DAWN_TICKET_PRICE * Config.SSQ_DAWN_TICKET_BUNDLE));
+							}
+							else
+							{
+								html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/ssq_notenoughticket.htm");
+							}
+						}
+						else
+						{
+							html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/ssq_notdawnorevent.htm");
+						}
+					}
+				}
+				else
+				{
+					html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noprivs.htm");
+				}
+				html.replace("%objectId%", String.valueOf(getObjectId()));
+				player.sendPacket(html);
+			}
+			else if (actualCommand.equalsIgnoreCase("manors_cert_confirm"))
+			{
+				final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
+				// Zoey76: TODO: Check privileges and replace with enum/constants.
+				if (isMyLord(player) || (validatePrivileges(player, 5) && (validateCondition(player) == COND_OWNER)))
+				{
+					if (getCastle().getSiege().getIsInProgress())
+					{
+						html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-busy.htm");
+						html.replace("%npcname%", String.valueOf(getName()));
+					}
+					else
+					{
+						final int cabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId());
+						if ((cabal == SevenSigns.CABAL_DAWN) && SevenSigns.getInstance().isCompetitionPeriod())
+						{
+							final int ticketCount = getCastle().getTicketBuyCount();
+							if (ticketCount < (Config.SSQ_DAWN_TICKET_QUANTITY / Config.SSQ_DAWN_TICKET_BUNDLE))
+							{
+								final long totalCost = Config.SSQ_DAWN_TICKET_PRICE * Config.SSQ_DAWN_TICKET_BUNDLE;
+								if (player.getAdena() >= totalCost)
+								{
+									// Take the adena.
+									player.reduceAdena(actualCommand, totalCost, this, true);
+									// Give the certificate.
+									player.addItem(actualCommand, Config.SSQ_MANORS_AGREEMENT_ID, Config.SSQ_DAWN_TICKET_BUNDLE, this, true);
+									// Set the ticket count for the player's clan.
+									getCastle().setTicketBuyCount(ticketCount + 1);
+									return;
+								}
+								html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain_noadena.htm");
+							}
+							else
+							{
+								html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/ssq_notenoughticket.htm");
+							}
+						}
+						else
+						{
+							html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/ssq_notdawnorevent.htm");
+						}
+					}
+				}
+				else
+				{
+					html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noprivs.htm");
+				}
+				html.replace("%objectId%", String.valueOf(getObjectId()));
+				player.sendPacket(html);
+			}
 			else
 			{
 				super.onBypassFeedback(player, command);

+ 6 - 17
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2SignsPriestInstance.java

@@ -24,7 +24,6 @@ import com.l2jserver.gameserver.SevenSigns;
 import com.l2jserver.gameserver.cache.HtmCache;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
-import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
 import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
@@ -186,21 +185,13 @@ public class L2SignsPriestInstance extends L2Npc
 					}
 					break;
 				case 34: // Pay the participation fee request
-					L2ItemInstance adena = player.getInventory().getItemByItemId(PcInventory.ADENA_ID); // adena
-					L2ItemInstance certif = player.getInventory().getItemByItemId(6388); // Lord of the Manor's Certificate of Approval
-					boolean fee = true;
-					
-					if ((player.getClassId().level() < 2) || ((adena != null) && (adena.getCount() >= SevenSigns.ADENA_JOIN_DAWN_COST)) || ((certif != null) && (certif.getCount() >= 1)))
-					{
-						fee = false;
-					}
-					if (fee)
+					if ((player.getClassId().level() > 1) && ((player.getAdena() >= Config.SSQ_JOIN_DAWN_ADENA_FEE) || (player.getInventory().getInventoryItemCount(Config.SSQ_MANORS_AGREEMENT_ID, -1) > 0)))
 					{
-						showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn_no.htm");
+						showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn.htm");
 					}
 					else
 					{
-						showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn.htm");
+						showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn_no.htm");
 					}
 					break;
 				case 3: // Join Cabal Intro 1
@@ -220,9 +211,7 @@ public class L2SignsPriestInstance extends L2Npc
 								return;
 							}
 						}
-						/*
-						 * If the player is trying to join the Lords of Dawn, check if they are carrying a Lord's certificate. If not then try to take the required amount of adena instead.
-						 */
+						// If the player is trying to join the Lords of Dawn, check if they are carrying a Lord's certificate. If not then try to take the required amount of adena instead.
 						if (Config.ALT_GAME_CASTLE_DAWN && (cabal == SevenSigns.CABAL_DAWN))
 						{
 							boolean allowJoinDawn = false;
@@ -231,11 +220,11 @@ public class L2SignsPriestInstance extends L2Npc
 							{
 								allowJoinDawn = true;
 							}
-							else if (player.destroyItemByItemId("SevenSigns", SevenSigns.CERTIFICATE_OF_APPROVAL_ID, 1, this, true))
+							else if (player.destroyItemByItemId("SevenSigns", Config.SSQ_MANORS_AGREEMENT_ID, 1, this, true))
 							{
 								allowJoinDawn = true;
 							}
-							else if (player.reduceAdena("SevenSigns", SevenSigns.ADENA_JOIN_DAWN_COST, this, true))
+							else if (player.reduceAdena("SevenSigns", Config.SSQ_JOIN_DAWN_ADENA_FEE, this, true))
 							{
 								allowJoinDawn = true;
 							}

+ 40 - 7
L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/Castle.java

@@ -65,12 +65,6 @@ public class Castle
 {
 	protected static final Logger _log = Logger.getLogger(Castle.class.getName());
 	
-	private List<CropProcure> _procure = new ArrayList<>();
-	private List<SeedProduction> _production = new ArrayList<>();
-	private List<CropProcure> _procureNext = new ArrayList<>();
-	private List<SeedProduction> _productionNext = new ArrayList<>();
-	private boolean _isNextPeriodApproved = false;
-	
 	private static final String CASTLE_MANOR_DELETE_PRODUCTION = "DELETE FROM castle_manor_production WHERE castle_id=?;";
 	private static final String CASTLE_MANOR_DELETE_PRODUCTION_PERIOD = "DELETE FROM castle_manor_production WHERE castle_id=? AND period=?;";
 	private static final String CASTLE_MANOR_DELETE_PROCURE = "DELETE FROM castle_manor_procure WHERE castle_id=?;";
@@ -99,6 +93,13 @@ public class Castle
 	private final Map<Integer, CastleFunction> _function;
 	private final List<L2Skill> _residentialSkills = new ArrayList<>();
 	private int _bloodAlliance = 0;
+	private int _ticketBuyCount = 0;
+	
+	private List<CropProcure> _procure = new ArrayList<>();
+	private List<SeedProduction> _production = new ArrayList<>();
+	private List<CropProcure> _procureNext = new ArrayList<>();
+	private List<SeedProduction> _productionNext = new ArrayList<>();
+	private boolean _isNextPeriodApproved = false;
 	
 	/** Castle Functions */
 	public static final int FUNC_TELEPORT = 1;
@@ -749,6 +750,8 @@ public class Castle
 					_showNpcCrest = rs.getBoolean("showNpcCrest");
 					
 					_bloodAlliance = rs.getInt("bloodAlliance");
+					
+					_ticketBuyCount = rs.getInt("ticketBuyCount");
 				}
 			}
 			_taxRate = _taxPercent / 100.0;
@@ -1577,7 +1580,37 @@ public class Castle
 			PreparedStatement statement = con.prepareStatement("UPDATE castle SET bloodAlliance = ? WHERE id = ?"))
 		{
 			statement.setInt(1, _bloodAlliance);
-			statement.setInt(2, getCastleId());
+			statement.setInt(2, _castleId);
+			statement.execute();
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * @return the tickets exchanged for this castle
+	 */
+	public int getTicketBuyCount()
+	{
+		return _ticketBuyCount;
+	}
+	
+	/**
+	 * Set the exchanged tickets count.<br>
+	 * Performs database update.
+	 * @param count the ticket count to set
+	 */
+	public void setTicketBuyCount(int count)
+	{
+		_ticketBuyCount = count;
+		
+		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
+			PreparedStatement statement = con.prepareStatement("UPDATE castle SET ticketBuyCount = ? WHERE id = ?"))
+		{
+			statement.setInt(1, _ticketBuyCount);
+			statement.setInt(2, _castleId);
 			statement.execute();
 		}
 		catch (Exception e)

+ 1 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/Siege.java

@@ -282,6 +282,7 @@ public class Siege implements Siegable
 				else
 				{
 					getCastle().setBloodAlliance(0);
+					getCastle().setTicketBuyCount(0);
 					for (L2ClanMember member : clan.getMembers())
 					{
 						if (member != null)