Jelajahi Sumber

BETA: Karma system rework:
* Implementing retail like formulas for increase/decrease karma.
* Added missing system message when karma has been decreased.
* Fixing minor typo since previous commit.
* Patch by: UnAfraid, Nos

Rumen Nikiforov 12 tahun lalu
induk
melakukan
5197487aa1

+ 2 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/GameServer.java

@@ -62,6 +62,7 @@ import com.l2jserver.gameserver.datatables.HerbDropTable;
 import com.l2jserver.gameserver.datatables.HitConditionBonus;
 import com.l2jserver.gameserver.datatables.InitialEquipmentData;
 import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.datatables.KarmaData;
 import com.l2jserver.gameserver.datatables.ManorData;
 import com.l2jserver.gameserver.datatables.MerchantPriceConfigTable;
 import com.l2jserver.gameserver.datatables.MultisellData;
@@ -238,6 +239,7 @@ public class GameServer
 		ClassListData.getInstance();
 		InitialEquipmentData.getInstance();
 		ExperienceTable.getInstance();
+		KarmaData.getInstance();
 		HitConditionBonus.getInstance();
 		CharTemplateTable.getInstance();
 		CharNameTable.getInstance();

+ 91 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/KarmaData.java

@@ -0,0 +1,91 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import com.l2jserver.gameserver.engines.DocumentParser;
+
+/**
+ * @author UnAfraid
+ */
+public class KarmaData extends DocumentParser
+{
+	private final Map<Integer, Double> _karmaTable = new HashMap<>();
+	
+	public KarmaData()
+	{
+		load();
+	}
+	
+	@Override
+	public synchronized void load()
+	{
+		parseDatapackFile("data/stats/chars/pcKarmaIncrease.xml");
+		_log.log(Level.INFO, getClass().getSimpleName() + ": Loaded " + _karmaTable.size() + " karma modifiers.");
+	}
+	
+	@Override
+	protected void parseDocument()
+	{
+		NamedNodeMap attrs;
+		for (Node n = getCurrentDocument().getFirstChild(); n != null; n = n.getNextSibling())
+		{
+			if ("pcKarmaIncrease".equalsIgnoreCase(n.getNodeName()))
+			{
+				for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
+				{
+					if ("increase".equalsIgnoreCase(d.getNodeName()))
+					{
+						attrs = d.getAttributes();
+						_karmaTable.put(parseInt(attrs, "lvl"), parseDouble(attrs, "val"));
+					}
+				}
+			}
+		}
+	}
+	
+	/**
+	 * @param level
+	 * @return {@code double} modifier used to calculate karma lost upon death.
+	 */
+	public double getMultiplier(int level)
+	{
+		return _karmaTable.get(level);
+	}
+	
+	/**
+	 * Gets the single instance of KarmaData.
+	 * @return single instance of KarmaData
+	 */
+	public static KarmaData getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	private static class SingletonHolder
+	{
+		protected static final KarmaData _instance = new KarmaData();
+	}
+}

+ 1 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Character.java

@@ -23,8 +23,6 @@ import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -207,7 +205,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder
 	private Calculator[] _calculators;
 	
 	/** Map containing all skills of this character. */
-	private final Map<Integer, L2Skill> _skills = Collections.synchronizedMap(new LinkedHashMap<Integer, L2Skill>());
+	private final Map<Integer, L2Skill> _skills = new FastMap<Integer, L2Skill>().shared();
 	
 	/** Map containing the active chance skills on this character */
 	private volatile ChanceSkillList _chanceSkills;

+ 15 - 151
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

@@ -5608,10 +5608,6 @@ public final class L2PcInstance extends L2Playable
 							deathPenalty(atWar, (pk != null), siegeNpc);
 						}
 					}
-					else if (!(isInsideZone(ZoneId.PVP) && !isInSiege()) || (pk == null))
-					{
-						onDieUpdateKarma(); // Update karma if delevel is not allowed
-					}
 				}
 			}
 		}
@@ -5766,60 +5762,22 @@ public final class L2PcInstance extends L2Playable
 		}
 	}
 	
-	private void onDieUpdateKarma()
-	{
-		// Karma lose for server that does not allow delevel
-		if (getKarma() > 0)
-		{
-			// this formula seems to work relatively well:
-			// baseKarma * thisLVL * (thisLVL/100)
-			// Calculate the new Karma of the attacker : newKarma = baseKarma*pkCountMulti*lvlDiffMulti
-			double karmaLost = Config.KARMA_LOST_BASE;
-			karmaLost *= getLevel(); // multiply by char lvl
-			karmaLost *= (getLevel() / 100.0); // divide by 0.charLVL
-			karmaLost = Math.round(karmaLost);
-			if (karmaLost < 0)
-			{
-				karmaLost = 1;
-			}
-			
-			// Decrease Karma of the L2PcInstance and Send it a Server->Client StatusUpdate packet with Karma and PvP Flag if necessary
-			setKarma(getKarma() - (int) karmaLost);
-		}
-	}
-	
 	public void onKillUpdatePvPKarma(L2Character target)
 	{
-		if (target == null)
-		{
-			return;
-		}
-		if (!(target instanceof L2Playable))
+		if ((target == null) || !target.isPlayable())
 		{
 			return;
 		}
 		
 		L2PcInstance targetPlayer = target.getActingPlayer();
-		
-		if (targetPlayer == null)
-		{
-			return; // Target player is null
-		}
-		if (targetPlayer == this)
+		if ((targetPlayer == null) || (targetPlayer == this))
 		{
-			return; // Target player is self
+			return;
 		}
 		
 		if (isCursedWeaponEquipped())
 		{
 			CursedWeaponsManager.getInstance().increaseKills(_cursedWeaponEquippedId);
-			// Custom message for time left
-			// CursedWeapon cw = CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquipedId);
-			// SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.THERE_IS_S1_HOUR_AND_S2_MINUTE_LEFT_OF_THE_FIXED_USAGE_TIME);
-			// int timeLeftInHours = (int)(((cw.getTimeLeft()/60000)/60));
-			// msg.addItemName(_cursedWeaponEquipedId);
-			// msg.addNumber(timeLeftInHours);
-			// sendPacket(msg);
 			return;
 		}
 		
@@ -5836,18 +5794,13 @@ public final class L2PcInstance extends L2Playable
 		}
 		
 		// Check if it's pvp
-		if ((checkIfPvP(target) && // Can pvp and
-		(targetPlayer.getPvpFlag() != 0 // Target player has pvp flag set
-		)) || // or
-		(isInsideZone(ZoneId.PVP) && // Player is inside pvp zone and
-		targetPlayer.isInsideZone(ZoneId.PVP) // Target player is inside pvp zone
-		))
+		if ((checkIfPvP(target) && (targetPlayer.getPvpFlag() != 0)) || (isInsideZone(ZoneId.PVP) && targetPlayer.isInsideZone(ZoneId.PVP)))
 		{
 			increasePvpKills(target);
 		}
 		else
-		// Target player doesn't have pvp flag set
 		{
+			// Target player doesn't have pvp flag set
 			// check about wars
 			if ((targetPlayer.getClan() != null) && (getClan() != null) && getClan().isAtWarWith(targetPlayer.getClanId()) && targetPlayer.getClan().isAtWarWith(getClanId()) && (targetPlayer.getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && (getPledgeType() != L2Clan.SUBUNIT_ACADEMY))
 			{
@@ -5867,8 +5820,7 @@ public final class L2PcInstance extends L2Playable
 			else if (targetPlayer.getPvpFlag() == 0) // Target player doesn't have karma
 			{
 				increasePkKillsAndKarma(target);
-				// Unequip adventurer items
-				checkItemRestriction();
+				checkItemRestriction(); // Unequip adventurer items
 			}
 		}
 	}
@@ -5896,114 +5848,26 @@ public final class L2PcInstance extends L2Playable
 	 */
 	public void increasePkKillsAndKarma(L2Character target)
 	{
-		int baseKarma = Config.KARMA_MIN_KARMA;
-		int newKarma = baseKarma;
-		int karmaLimit = Config.KARMA_MAX_KARMA;
-		
-		int pkLVL = getLevel();
-		int pkPKCount = getPkKills();
-		
-		int targLVL = target.getLevel();
-		
-		int lvlDiffMulti = 0;
-		int pkCountMulti = 0;
-		
-		// Check if the attacker has a PK counter greater than 0
-		if (pkPKCount > 0)
+		// Only playables can increase karma/pk
+		if ((target == null) || !target.isPlayable())
 		{
-			pkCountMulti = pkPKCount / 2;
-		}
-		else
-		{
-			pkCountMulti = 1;
-		}
-		
-		if (pkCountMulti < 1)
-		{
-			pkCountMulti = 1;
-		}
-		
-		// Calculate the level difference Multiplier between attacker and killed L2PcInstance
-		if (pkLVL > targLVL)
-		{
-			lvlDiffMulti = pkLVL / targLVL;
-		}
-		else
-		{
-			lvlDiffMulti = 1;
-		}
-		
-		if (lvlDiffMulti < 1)
-		{
-			lvlDiffMulti = 1;
-		}
-		
-		// Calculate the new Karma of the attacker : newKarma = baseKarma*pkCountMulti*lvlDiffMulti
-		newKarma *= pkCountMulti;
-		newKarma *= lvlDiffMulti;
-		
-		// Make sure newKarma is less than karmaLimit and higher than baseKarma
-		if (newKarma < baseKarma)
-		{
-			newKarma = baseKarma;
-		}
-		if (newKarma > karmaLimit)
-		{
-			newKarma = karmaLimit;
-		}
-		
-		// Fix to prevent overflow (=> karma has a max value of 2 147 483 647)
-		if (getKarma() > (Integer.MAX_VALUE - newKarma))
-		{
-			newKarma = Integer.MAX_VALUE - getKarma();
+			return;
 		}
 		
-		// Add karma to attacker and increase its PK counter
-		setKarma(getKarma() + newKarma);
-		if ((target instanceof L2PcInstance) && AntiFeedManager.getInstance().check(this, target))
+		// PK Points are increased only if you kill a player.
+		if (target.isPlayer())
 		{
 			setPkKills(getPkKills() + 1);
 		}
 		
-		// Send a Server->Client UserInfo packet to attacker with its Karma and PK Counter
+		// Calculate new karma.
+		setKarma(getKarma() + Formulas.calculateKarmaGain(getPkKills(), target.isSummon()));
+		
+		// Update player's UI.
 		sendPacket(new UserInfo(this));
 		sendPacket(new ExBrExtraUserInfo(this));
 	}
 	
-	public int calculateKarmaLost(long exp)
-	{
-		// KARMA LOSS
-		// When a PKer gets killed by another player or a L2MonsterInstance, it loses a certain amount of Karma based on their level.
-		// this (with defaults) results in a level 1 losing about ~2 karma per death, and a lvl 70 loses about 11760 karma per death...
-		// You lose karma as long as you were not in a pvp zone and you did not kill urself.
-		// NOTE: exp for death (if delevel is allowed) is based on the players level
-		
-		long expGained = Math.abs(exp);
-		expGained /= Config.KARMA_XP_DIVIDER;
-		
-		// FIXME Micht : Maybe this code should be fixed and karma set to a long value
-		int karmaLost = 0;
-		if (expGained > Integer.MAX_VALUE)
-		{
-			karmaLost = Integer.MAX_VALUE;
-		}
-		else
-		{
-			karmaLost = (int) expGained;
-		}
-		
-		if (karmaLost < Config.KARMA_LOST_BASE)
-		{
-			karmaLost = Config.KARMA_LOST_BASE;
-		}
-		if (karmaLost > getKarma())
-		{
-			karmaLost = getKarma();
-		}
-		
-		return karmaLost;
-	}
-	
 	public void updatePvPStatus()
 	{
 		if (isInsideZone(ZoneId.PVP))

+ 5 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java

@@ -31,6 +31,7 @@ import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PetInstance;
 import com.l2jserver.gameserver.model.entity.RecoBonus;
 import com.l2jserver.gameserver.model.quest.QuestState;
+import com.l2jserver.gameserver.model.stats.Formulas;
 import com.l2jserver.gameserver.model.stats.MoveType;
 import com.l2jserver.gameserver.model.stats.Stats;
 import com.l2jserver.gameserver.model.zone.ZoneId;
@@ -94,10 +95,13 @@ public class PcStat extends PlayableStat
 		// Set new karma
 		if (!activeChar.isCursedWeaponEquipped() && (activeChar.getKarma() > 0) && (activeChar.isGM() || !activeChar.isInsideZone(ZoneId.PVP)))
 		{
-			int karmaLost = activeChar.calculateKarmaLost(value);
+			int karmaLost = Formulas.calculateKarmaLost(activeChar, value);
 			if (karmaLost > 0)
 			{
 				activeChar.setKarma(activeChar.getKarma() - karmaLost);
+				final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOUR_KARMA_HAS_BEEN_CHANGED_TO_S1);
+				msg.addNumber(activeChar.getKarma());
+				activeChar.sendPacket(msg);
 			}
 		}
 		

+ 38 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/stats/Formulas.java

@@ -26,6 +26,7 @@ import com.l2jserver.Config;
 import com.l2jserver.gameserver.SevenSigns;
 import com.l2jserver.gameserver.SevenSignsFestival;
 import com.l2jserver.gameserver.datatables.HitConditionBonus;
+import com.l2jserver.gameserver.datatables.KarmaData;
 import com.l2jserver.gameserver.instancemanager.CastleManager;
 import com.l2jserver.gameserver.instancemanager.ClanHallManager;
 import com.l2jserver.gameserver.instancemanager.FortManager;
@@ -2308,4 +2309,41 @@ public final class Formulas
 		return Rnd.get(100) < (((((skill.getMagicLevel() + baseChance) - target.getLevel()) + 30) - target.getINT()) * Formulas.calcElemental(attacker, target, skill));
 	}
 	
+	/**
+	 * Calculates karma lost upon death.
+	 * @param player
+	 * @param exp
+	 * @return the amount of karma player has loosed.
+	 */
+	public static int calculateKarmaLost(L2PcInstance player, long exp)
+	{
+		double karmaLooseMul = KarmaData.getInstance().getMultiplier(player.getLevel());
+		return (int) ((Math.abs(exp) / karmaLooseMul) / 15);
+	}
+	
+	/**
+	 * Calculates karma gain upon player kill.
+	 * @param pkCount
+	 * @param isSummon
+	 * @return karma points that will be added to the player.
+	 */
+	public static int calculateKarmaGain(int pkCount, boolean isSummon)
+	{
+		int result = 14400;
+		if (pkCount < 100)
+		{
+			result = (int) (((((pkCount - 1) * 0.5) + 1) * 60) * 4);
+		}
+		else if (pkCount < 180)
+		{
+			result = (int) (((((pkCount + 1) * 0.125) + 37.5) * 60) * 4);
+		}
+		
+		if (isSummon)
+		{
+			result = ((pkCount & 3) + result) >> 2;
+		}
+		
+		return result;
+	}
 }