Conditional Buffs & M. Magnet & Loot Patch + Exp Split + Duey Rework

Fixed Monster Magnet skill when used on bosses disconnecting the caster.
Improved conditional buff system, no longer updating buffs that are not supposed to toggle.
Added party hunting in the conditional buffs system.
Refactored usage of DB by Duey. Registered Duey items now make use of the same table as the other inventory items.
Fixed non-encapsulated unlocking in reactor class.
Fixed stylish NPCs disconnecting players when trying to display empty styles list.
Fixed a deadlock case within recently implemented update buff effects (conditional buffs mechanic).
Fixed AOE mobskills not behaving well for fixed mobs (those shouldn't take into account attribute "facingLeft").
Fixed non-flipping mobs having attribute "facingLeft" updated according to controller position.
Revised aggro system no longer having bosses expire player chase.
Fixed chalkboard being depleted upon use.
Refactored MapleMapFactory, looking for normalization of the Factory design pattern the class was intended to make use at its conception.
Added MP replenishing system for mobs, gains based on its level.
Fixed indisponibility of one-of-a-kind loots due to the killer's team already having one sample each.
Reworked the EXP split system within the source. New behavior is expected to be GMS-like.
Adjusted interaction within the NPC Nein Spirit's Baby Dragon area. Only players who interacts with quests within can access the area now. One player at a time, with a timeout timer.
Fixed check of level requisites for expeditions.
This commit is contained in:
ronancpl
2019-06-15 15:10:56 -03:00
parent 04b11e2518
commit a39a210c1f
54 changed files with 1217 additions and 866 deletions
@@ -24,21 +24,22 @@ package server;
import client.inventory.Item;
import java.util.Calendar;
public class DueyPackages {
public class DueyPackage {
private String sender = null;
private Item item = null;
private int mesos = 0;
private String message = "";
private int day;
private int month;
private int year;
private int packageId = 0;
public DueyPackages(int pId, Item item) {
public DueyPackage(int pId, Item item) {
this.item = item;
packageId = pId;
}
public DueyPackages(int pId) { // Meso only package.
public DueyPackage(int pId) { // Meso only package.
this.packageId = pId;
}
@@ -61,6 +62,14 @@ public class DueyPackages {
public void setMesos(int set) {
mesos = set;
}
public String getMessage() {
return message;
}
public void setMessage(String m) {
message = m;
}
public int getPackageId() {
return packageId;
@@ -1586,6 +1586,18 @@ public class MapleItemInformationProvider {
eq.getWatk() > 0 || eq.getMatk() > 0 || eq.getWdef() > 0 || eq.getMdef() > 0 || eq.getAcc() > 0 ||
eq.getAvoid() > 0 || eq.getSpeed() > 0 || eq.getJump() > 0 || eq.getHp() > 0 || eq.getMp() > 0);
}
public boolean isUnmerchable(int itemId) {
if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && isCash(itemId)) {
return true;
}
if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(itemId)) {
return true;
}
return false;
}
public Collection<Item> canWearEquipment(MapleCharacter chr, Collection<Item> items) {
MapleInventory inv = chr.getInventory(MapleInventoryType.EQUIPPED);
+38 -17
View File
@@ -150,12 +150,14 @@ public class MapleStatEffect {
private static class CardItemupStats {
protected int itemCode, prob;
protected boolean party;
private List<Pair<Integer, Integer>> areas;
private CardItemupStats(int code, int prob, List<Pair<Integer, Integer>> areas) {
private CardItemupStats(int code, int prob, List<Pair<Integer, Integer>> areas, boolean inParty) {
this.itemCode = code;
this.prob = prob;
this.areas = areas;
this.party = inParty;
}
private boolean isInArea(int mapid) {
@@ -173,8 +175,22 @@ public class MapleStatEffect {
}
}
public boolean isActive(int mapid) {
return cardStats == null || cardStats.isInArea(mapid);
private boolean isEffectActive(int mapid, boolean partyHunting) {
if (cardStats == null) return true;
if (!cardStats.isInArea(mapid)) {
return false;
}
if (cardStats.party && !partyHunting) {
return false;
}
return true;
}
public boolean isActive(MapleCharacter applyto) {
return isEffectActive(applyto.getMapId(), applyto.getPartyMembersOnSameMap().size() > 1);
}
public int getCardRate(int mapid, int itemid) {
@@ -354,16 +370,27 @@ public class MapleStatEffect {
} else if (isMonsterCard(sourceid)) {
int prob = 0, itemupCode = Integer.MAX_VALUE;
List<Pair<Integer, Integer>> areas = null;
boolean inParty = false;
MapleData con = source.getChildByPath("con");
if (con != null) {
areas = new ArrayList<>(3);
for (MapleData conData : con.getChildren()) {
int startMap = MapleDataTool.getInt("sMap", conData, 0);
int endMap = MapleDataTool.getInt("eMap", conData, 0);
int type = MapleDataTool.getInt("type", conData, -1);
if (type == 0) {
int startMap = MapleDataTool.getInt("sMap", conData, 0);
int endMap = MapleDataTool.getInt("eMap", conData, 0);
areas.add(new Pair<>(startMap, endMap));
areas.add(new Pair<>(startMap, endMap));
} else if (type == 2) {
inParty = true;
}
}
if (areas.isEmpty()) {
areas = null;
}
}
@@ -409,7 +436,7 @@ public class MapleStatEffect {
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.MAP_PROTECTION, thaw > 0 ? 1 : 2);
}
ret.cardStats = new CardItemupStats(itemupCode, prob, areas);
ret.cardStats = new CardItemupStats(itemupCode, prob, areas, inParty);
} else if (isExpIncrease(sourceid)) {
addBuffStatPairToListIfNotZero(statups, MapleBuffStat.EXP_INCREASE, MapleDataTool.getInt("expinc", source, 0));
}
@@ -1180,15 +1207,9 @@ public class MapleStatEffect {
}
private Rectangle calculateBoundingBox(Point posFrom, boolean facingLeft) {
Point mylt;
Point myrb;
if (facingLeft) {
mylt = new Point(lt.x + posFrom.x, lt.y + posFrom.y);
myrb = new Point(rb.x + posFrom.x, rb.y + posFrom.y);
} else {
myrb = new Point(-lt.x + posFrom.x, rb.y + posFrom.y);
mylt = new Point(-rb.x + posFrom.x, lt.y + posFrom.y);
}
int multiplier = facingLeft ? 1 : -1;
Point mylt = new Point(lt.x * multiplier + posFrom.x, lt.y + posFrom.y);
Point myrb = new Point(rb.x * multiplier + posFrom.x, rb.y + posFrom.y);
Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
return bounds;
}
@@ -1313,7 +1334,7 @@ public class MapleStatEffect {
if (localstatups.size() > 0) {
byte[] buff = null;
byte[] mbuff = null;
if (getSummonMovementType() == null && this.isActive(applyto.getMapId())) {
if (getSummonMovementType() == null && this.isActive(applyto)) {
buff = MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), localDuration, localstatups);
}
if (isDash()) {
+9
View File
@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.life;
import java.awt.Point;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@@ -216,6 +217,14 @@ public class MapleLifeFactory {
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
}
int noFlip = MapleDataTool.getInt("noFlip", monsterInfoData, 0);
if (noFlip > 0) {
Point origin = MapleDataTool.getPoint("stand/0/origin", monsterData, null);
if (origin != null) {
stats.setFixedStance(origin.getX() < 1 ? 5 : 4); // fixed left/right
}
}
return new Pair<>(stats, attackInfos);
}
+172 -153
View File
@@ -56,7 +56,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.channel.Channel;
import net.server.world.World;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import scripting.event.EventInstanceManager;
@@ -64,6 +63,7 @@ import server.TimerManager;
import server.life.MapleLifeFactory.BanishInfo;
import server.maps.MapleMap;
import server.maps.MapleMapObjectType;
import tools.IntervalBuilder;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
@@ -72,6 +72,7 @@ import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.coordinator.MapleMonsterAggroCoordinator;
import server.MapleStatEffect;
import server.loot.MapleLootManager;
import server.maps.MapleSummon;
public class MapleMonster extends AbstractLoadedMapleLife {
@@ -94,12 +95,11 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private Map<Pair<Integer, Integer>, Integer> skillsUsed = new HashMap<>();
private Set<Integer> usedAttacks = new HashSet<>();
private Set<Integer> calledMobOids = null;
private int calledMobCount = 0;
private WeakReference<MapleMonster> callerMob = new WeakReference<>(null);
private List<Integer> stolenItems = new ArrayList<>();
private int team;
private int parentMobOid = 0;
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
private final HashMap<Integer, AtomicLong> takenDamage = new HashMap<>();
private Runnable removeAfterAction = null;
private boolean availablePuppetUpdate = true;
@@ -160,13 +160,17 @@ public class MapleMonster extends AbstractLoadedMapleLife {
this.parentMobOid = parentMobId;
}
public int countAvailableMobSummons(int limit, int skillLimit) { // limit prop for summons has another conotation, found thanks to MedicOP
public int countAvailableMobSummons(int summonsSize, int skillLimit) { // limit prop for summons has another conotation, found thanks to MedicOP
int summonsCount;
Set<Integer> calledOids = this.calledMobOids;
if(calledOids != null) {
limit -= calledOids.size();
summonsCount = calledOids.size();
} else {
summonsCount = 0;
}
return Math.min(limit, skillLimit - this.calledMobCount);
return Math.min(summonsSize, skillLimit - summonsCount);
}
public void addSummonedMob(MapleMonster mob) {
@@ -178,7 +182,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
calledOids.add(mob.getObjectId());
mob.setSummonerMob(this);
this.calledMobCount += 1;
}
private void removeSummonedMob(int mobOid) {
@@ -442,7 +445,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
dispatchMonsterDamaged(from, trueDamage);
if (!takenDamage.containsKey(from.getId())) {
takenDamage.put(from.getId(), new AtomicInteger(trueDamage));
takenDamage.put(from.getId(), new AtomicLong(trueDamage));
} else {
takenDamage.get(from.getId()).addAndGet(trueDamage);
}
@@ -475,98 +478,85 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return takenDamage.containsKey(chr.getId());
}
private void distributeExperienceToParty(int pid, float exp, int mostDamageCid, int minThresholdLevel, int killerLevel, Set<MapleCharacter> underleveled, Map<MapleCharacter, Float> partyExpReward, Set<MapleCharacter> participants) {
MapleCharacter pchar = getMap().getAnyCharacterFromParty(pid); // thanks G h o s t, Alfred, Vcoc, BHB for poiting out a bug in detecting party members after membership transactions in a party took place
List<MapleCharacter> members;
if (pchar != null) {
members = pchar.getPartyMembersOnSameMap();
} else {
members = new LinkedList<>();
private static boolean isWhiteExpGain(MapleCharacter chr, Map<Integer, Float> personalRatio, double sdevRatio) {
Float pr = personalRatio.get(chr.getId());
if (pr == null) {
return false;
}
List<MapleCharacter> expSharers = new LinkedList<>();
int expParticipantsMaxLevel = 1;
boolean hasMostDamageCid = false;
for (MapleCharacter mc : members) {
if (mc.getId() == mostDamageCid) {
hasMostDamageCid = true;
}
if (mc.getLevel() >= minThresholdLevel) { //NO EXP WILL BE GIVEN for those who are underleveled!
if (Math.abs(killerLevel - mc.getLevel()) < ServerConstants.MIN_RANGELEVEL_TO_EXP_LEECH) {
// thanks Thora for pointing out leech level limitation
if (expParticipantsMaxLevel < mc.getLevel() && participants.contains(mc)) {
expParticipantsMaxLevel = mc.getLevel();
}
expSharers.add(mc);
}
} else {
underleveled.add(mc);
}
}
int numExpSharers = expSharers.size();
// PARTY BONUS: 2p -> +5% , 3p -> +6.25% , 4p -> +7.5% , 5p -> +8.75% , 6p -> +10%
// MOST DAMAGE BONUS: 1.5x bonus
// thanks Crypter for reporting an insufficiency on party exp bonuses
final float partyModifier = numExpSharers <= 1 ? 0.0f : 0.05f + (0.0125f * (numExpSharers - 1));
final float mostDamageModifier = hasMostDamageCid ? 1.5f : 1.0f;
final float partyExp = exp * partyModifier * mostDamageModifier;
for (MapleCharacter mc : expSharers) {
float levelPenaltyModifier = (float) Math.min(1.0, Math.sqrt(((float) mc.getLevel()) / expParticipantsMaxLevel));
partyExpReward.put(mc, partyExp * levelPenaltyModifier);
}
}
private int calcThresholdLevel(boolean isPqMob) {
if(!ServerConstants.USE_ENFORCE_MOB_LEVEL_RANGE) {
return 0;
} else if (isPqMob) {
double thresholdLevel = getLevel();
thresholdLevel /= 32.55916838;
thresholdLevel = Math.log(thresholdLevel) / 0.02058204546;
return (int) Math.ceil(thresholdLevel);
} else {
return getLevel() - (!isBoss() ? ServerConstants.MIN_UNDERLEVEL_TO_EXP_GAIN : 2 * ServerConstants.MIN_UNDERLEVEL_TO_EXP_GAIN);
}
return pr >= sdevRatio;
}
private static double calcExperienceStandDevThreshold(Map<MapleCharacter, Float> personalExpReward, float exp2) {
private static double calcExperienceStandDevThreshold(List<Float> entryExpRatio, int totalEntries) {
float avgExpReward = 0.0f;
for (Float exp : personalExpReward.values()) {
for (Float exp : entryExpRatio) {
avgExpReward += exp;
}
// thanks Simon for finding an issue with solo party player gaining yellow EXP when soloing mobs
float realAvgExpReward = avgExpReward;
avgExpReward -= exp2; // clear out the 20% raw exp from last hitting
avgExpReward /= personalExpReward.size();
avgExpReward /= totalEntries;
float varExpReward = 0.0f;
for (Float exp : personalExpReward.values()) {
varExpReward += Math.pow(exp - realAvgExpReward, 2);
for (Float exp : entryExpRatio) {
varExpReward += Math.pow(exp - avgExpReward, 2);
}
varExpReward /= personalExpReward.size();
varExpReward /= entryExpRatio.size();
return avgExpReward + Math.sqrt(varExpReward);
}
private void propagateExperienceGains(Map<MapleCharacter, Float> personalExpReward, Map<MapleCharacter, Float> partyExpReward, float exp2) {
Set<MapleCharacter> expRewardPlayers = new HashSet<>(personalExpReward.keySet());
expRewardPlayers.addAll(partyExpReward.keySet());
private void distributePlayerExperience(MapleCharacter chr, float exp, float partyBonusMod, int totalPartyLevel, boolean highestPartyDamager, boolean whiteExpGain) {
float playerExp = (ServerConstants.EXP_SPLIT_COMMON_MOD * chr.getLevel()) / totalPartyLevel;
if (highestPartyDamager) playerExp += ServerConstants.EXP_SPLIT_MVP_MOD;
double sdevExp = calcExperienceStandDevThreshold(personalExpReward, exp2);
for (MapleCharacter chr : expRewardPlayers) {
Float personalExp = personalExpReward.get(chr);
Float partyExp = partyExpReward.get(chr);
playerExp *= exp;
float bonusExp = partyBonusMod * playerExp;
this.giveExpToCharacter(chr, playerExp, bonusExp, whiteExpGain);
}
private void distributePartyExperience(Map<MapleCharacter, Long> partyParticipation, float expPerDmg, Set<MapleCharacter> underleveled, Map<Integer, Float> personalRatio, double sdevRatio) {
IntervalBuilder leechInterval = new IntervalBuilder();
leechInterval.addInterval(this.getLevel() - ServerConstants.EXP_SPLIT_LEVEL_INTERVAL, this.getLevel() + ServerConstants.EXP_SPLIT_LEVEL_INTERVAL);
long maxDamage = 0, partyDamage = 0;
MapleCharacter participationMvp = null;
for (Entry<MapleCharacter, Long> e : partyParticipation.entrySet()) {
long entryDamage = e.getValue();
partyDamage += entryDamage;
this.giveExpToCharacter(chr, personalExp, partyExp, personalExp != null && personalExp >= sdevExp);
if (maxDamage < entryDamage) {
maxDamage = entryDamage;
participationMvp = e.getKey();
}
// thanks Thora for pointing out leech level limitation
int chrLevel = e.getKey().getLevel();
leechInterval.addInterval(chrLevel - ServerConstants.EXP_SPLIT_LEECH_INTERVAL, chrLevel + ServerConstants.EXP_SPLIT_LEECH_INTERVAL);
}
List<MapleCharacter> expMembers = new LinkedList<>();
int totalPartyLevel = 0;
// thanks G h o s t, Alfred, Vcoc, BHB for poiting out a bug in detecting party members after membership transactions in a party took place
for (MapleCharacter member : partyParticipation.keySet().iterator().next().getPartyMembersOnSameMap()) {
if (!leechInterval.inInterval(member.getLevel())) {
underleveled.add(member);
continue;
}
totalPartyLevel += member.getLevel();
expMembers.add(member);
}
int membersSize = expMembers.size();
float participationExp = partyDamage * expPerDmg;
// thanks Crypter for reporting an insufficiency on party exp bonuses
float partyBonusMod = (membersSize > 1) ? 0.05f * membersSize : 0.0f;
for (MapleCharacter mc : expMembers) {
distributePlayerExperience(mc, participationExp, partyBonusMod, totalPartyLevel, mc == participationMvp, isWhiteExpGain(mc, personalRatio, sdevRatio));
}
}
@@ -575,84 +565,89 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return;
}
Map<MapleCharacter, Float> personalExpReward = new HashMap<>();
Map<MapleCharacter, Float> partyExpReward = new HashMap<>();
Map<MapleParty, Map<MapleCharacter, Long>> partyExpDist = new HashMap<>();
Map<MapleCharacter, Long> soloExpDist = new HashMap<>();
Map<Integer, MapleCharacter> mapPlayers = map.getMapAllPlayers();
int totalEntries = 0; // counts "participant parties", players who no longer are available in the map is an "independent party"
for (Entry<Integer, AtomicLong> e : takenDamage.entrySet()) {
MapleCharacter chr = mapPlayers.get(e.getKey());
if (chr != null) {
long damage = e.getValue().longValue();
MapleParty p = chr.getParty();
if (p != null) {
Map<MapleCharacter, Long> partyParticipation = partyExpDist.get(p);
if (partyParticipation == null) {
partyParticipation = new HashMap<>(6);
partyExpDist.put(p, partyParticipation);
totalEntries += 1;
}
partyParticipation.put(chr, damage);
} else {
soloExpDist.put(chr, damage);
totalEntries += 1;
}
} else {
totalEntries += 1;
}
}
long totalDamage = maxHpPlusHeal.get();
int mobExp = getExp();
float expPerDmg = ((float) mobExp) / totalDamage;
Map<Integer, Float> personalRatio = new HashMap<>();
List<Float> entryExpRatio = new LinkedList<>();
for (Entry<MapleCharacter, Long> e : soloExpDist.entrySet()) {
float ratio = ((float) e.getValue()) / totalDamage;
personalRatio.put(e.getKey().getId(), ratio);
entryExpRatio.add(ratio);
}
for (Map<MapleCharacter, Long> m : partyExpDist.values()) {
float ratio = 0.0f;
for (Entry<MapleCharacter, Long> e : m.entrySet()) {
float chrRatio = ((float) e.getValue()) / totalDamage;
personalRatio.put(e.getKey().getId(), chrRatio);
ratio += chrRatio;
}
entryExpRatio.add(ratio);
}
double sdevRatio = calcExperienceStandDevThreshold(entryExpRatio, totalEntries);
// GMS-like player and party split calculations found thanks to Russt, KaidaTan, Dusk, AyumiLove. Src: https://ayumilovemaple.wordpress.com/maplestory_calculator_formula/
Set<MapleCharacter> underleveled = new HashSet<>();
for (Entry<MapleCharacter, Long> chrParticipation : soloExpDist.entrySet()) {
float exp = chrParticipation.getValue() * expPerDmg;
MapleCharacter chr = chrParticipation.getKey();
distributePlayerExperience(chr, exp, 0.0f, chr.getLevel(), true, isWhiteExpGain(chr, personalRatio, sdevRatio));
}
for (Map<MapleCharacter, Long> partyParticipation : partyExpDist.values()) {
distributePartyExperience(partyParticipation, expPerDmg, underleveled, personalRatio, sdevRatio);
}
EventInstanceManager eim = getMap().getEventInstance();
int minThresholdLevel = calcThresholdLevel(eim != null), killerLevel = Integer.MAX_VALUE;
int exp = getExp();
long totalHealth = maxHpPlusHeal.get();
Map<Integer, Float> expDist = new HashMap<>();
Map<Integer, Float> partyExp = new HashMap<>();
float exp8perHp = (0.8f * exp) / totalHealth; // 80% of pool is split amongst all the damagers
float exp2 = (0.2f * exp); // 20% of pool goes to the killer or his/her party
for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
expDist.put(damage.getKey(), exp8perHp * damage.getValue().get());
}
Set<MapleCharacter> underleveled = new HashSet<>();
Collection<MapleCharacter> mapChrs = map.getCharacters();
for (MapleCharacter mc : mapChrs) {
Float mcExp = expDist.remove(mc.getId());
if (mcExp != null) {
float xp = mcExp;
boolean isKiller = (mc.getId() == killerId);
if (isKiller) {
if (eim != null) {
eim.monsterKilled(mc, this);
}
killerLevel = mc.getLevel();
xp += exp2;
}
if(mc.getLevel() >= minThresholdLevel) {
//NO EXP WILL BE GIVEN for those who are underleveled!
personalExpReward.put(mc, xp);
MapleParty p = mc.getParty();
if (p != null) { // for party bonus exp
int pID = p.getId();
float pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
partyExp.put(pID, pXP);
}
} else {
underleveled.add(mc);
}
if (eim != null) {
MapleCharacter chr = mapPlayers.get(killerId);
if (chr != null) {
eim.monsterKilled(chr, this);
}
}
if(!expDist.isEmpty()) { // locate on world server the partyid of the missing characters
World wserv = map.getWorldServer();
for (Entry<Integer, Float> ed : expDist.entrySet()) {
boolean isKiller = (ed.getKey() == killerId);
float xp = ed.getValue();
if (isKiller) {
xp += exp2;
}
Integer pID = wserv.getCharacterPartyid(ed.getKey());
if (pID != null) {
float pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
partyExp.put(pID, pXP);
}
}
}
Set<MapleCharacter> participants = personalExpReward.keySet();
int mostDamageCid = this.getHighestDamagerId();
for (Entry<Integer, Float> party : partyExp.entrySet()) {
distributeExperienceToParty(party.getKey(), party.getValue(), mostDamageCid, minThresholdLevel, killerLevel, underleveled, partyExpReward, participants);
}
for(MapleCharacter mc : underleveled) {
mc.showUnderleveledInfo(this);
}
propagateExperienceGains(personalExpReward, partyExpReward, killerLevel != Integer.MAX_VALUE ? exp2 : 0.0f);
}
private float getStatusExpMultiplier(MapleCharacter attacker) {
@@ -718,6 +713,20 @@ public class MapleMonster extends AbstractLoadedMapleLife {
attacker.updateQuestMobCount(getId());
}
}
public List<MonsterDropEntry> retrieveRelevantDrops() {
Map<Integer, MapleCharacter> pchars = map.getMapAllPlayers();
List<MapleCharacter> lootChars = new LinkedList<>();
for (Integer cid : takenDamage.keySet()) {
MapleCharacter chr = pchars.get(cid);
if (chr != null && chr.isLoggedinWorld()) {
lootChars.add(chr);
}
}
return MapleLootManager.retrieveRelevantDrops(this.getId(), lootChars);
}
public MapleCharacter killBy(final MapleCharacter killer) {
distributeExperience(killer != null ? killer.getId() : 0);
@@ -904,9 +913,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
public int getHighestDamagerId() {
int curId = 0;
int curDmg = 0;
long curDmg = 0;
for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
for (Entry<Integer, AtomicLong> damage : takenDamage.entrySet()) {
curId = damage.getValue().get() >= curDmg ? damage.getKey() : curId;
curDmg = damage.getKey() == curId ? damage.getValue().get() : curDmg;
}
@@ -1012,6 +1021,16 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return stats.isMobile();
}
@Override
public boolean isFacingLeft() {
int fixedStance = stats.getFixedStance(); // thanks DimDiDima for noticing inconsistency on some AOE mobskills
if (fixedStance != 0) {
return Math.abs(fixedStance) % 2 == 1;
}
return super.isFacingLeft();
}
public ElementalEffectiveness getElementalEffectiveness(Element e) {
statiLock.lock();
try {
+9
View File
@@ -50,6 +50,7 @@ public class MapleMonsterStats {
public BanishInfo banish = null;
public List<loseItem> loseItem = null;
public selfDestruction selfDestruction = null;
public int fixedStance = 0;
public boolean friendly;
public void setChange(boolean change) {
@@ -339,6 +340,14 @@ public class MapleMonsterStats {
this.MDDamage = MDDamage;
}
public int getFixedStance() {
return this.fixedStance;
}
public void setFixedStance(int stance) {
this.fixedStance = stance;
}
public MapleMonsterStats copy() {
MapleMonsterStats copy = new MapleMonsterStats();
try {
+6 -7
View File
@@ -197,7 +197,7 @@ public class MobSkill {
}
break;
case 131: // Mist
monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition(), true), monster, this), x * 100, false, false, false);
monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), monster, this), x * 100, false, false, false);
break;
case 132:
disease = MapleDisease.CONFUSE;
@@ -253,9 +253,9 @@ public class MobSkill {
List<Integer> summons = getSummons();
int summonLimit = monster.countAvailableMobSummons(summons.size(), skillLimit);
if (summonLimit >= 1) {
Collections.shuffle(summons);
boolean bossRushMap = GameConstants.isBossRush(map.getId());
Collections.shuffle(summons);
for (Integer mobId : summons.subList(0, summonLimit)) {
MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId);
if (toSpawn != null) {
@@ -412,12 +412,11 @@ public class MobSkill {
int multiplier = facingLeft ? 1 : -1;
Point mylt = new Point(lt.x * multiplier + posFrom.x, lt.y + posFrom.y);
Point myrb = new Point(rb.x * multiplier + posFrom.x, rb.y + posFrom.y);
return new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
return bounds;
}
private List<MapleMapObject> getObjectsInRange(MapleMonster monster, MapleMapObjectType objectType) {
List<MapleMapObjectType> objectTypes = new ArrayList<MapleMapObjectType>();
objectTypes.add(objectType);
return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), objectTypes);
return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), Collections.singletonList(objectType));
}
}
+15 -15
View File
@@ -23,7 +23,7 @@ import client.MapleCharacter;
import java.util.LinkedList;
import java.util.List;
import server.MapleItemInformationProvider;
//import server.MapleItemInformationProvider;
import server.life.MapleMonsterInformationProvider;
import server.life.MonsterDropEntry;
import server.quest.MapleQuest;
@@ -34,7 +34,7 @@ import server.quest.MapleQuest;
*/
public class MapleLootManager {
private static boolean isRelevantDrop(MonsterDropEntry dropEntry, List<MapleCharacter> partyMembers, List<MapleLootInventory> partyInv) {
private static boolean isRelevantDrop(MonsterDropEntry dropEntry, List<MapleCharacter> players, List<MapleLootInventory> playersInv) {
int qStartAmount = 0, qCompleteAmount = 0;
MapleQuest quest = MapleQuest.getInstance(dropEntry.questid);
if (quest != null) {
@@ -42,12 +42,12 @@ public class MapleLootManager {
qCompleteAmount = quest.getCompleteItemAmountNeeded(dropEntry.itemId);
}
boolean restricted = MapleItemInformationProvider.getInstance().isPickupRestricted(dropEntry.itemId);
for (int i = 0; i < partyMembers.size(); i++) {
MapleLootInventory chrInv = partyInv.get(i);
//boolean restricted = MapleItemInformationProvider.getInstance().isPickupRestricted(dropEntry.itemId);
for (int i = 0; i < players.size(); i++) {
MapleLootInventory chrInv = playersInv.get(i);
if (dropEntry.questid > 0) {
int qItemAmount, chrQuestStatus = partyMembers.get(i).getQuestStatus(dropEntry.questid);
int qItemAmount, chrQuestStatus = players.get(i).getQuestStatus(dropEntry.questid);
if (chrQuestStatus == 0) {
qItemAmount = qStartAmount;
} else if (chrQuestStatus != 1) {
@@ -63,12 +63,12 @@ public class MapleLootManager {
int qItemStatus = chrInv.hasItem(dropEntry.itemId, qItemAmount);
if (qItemStatus == 2) {
continue;
} else if (restricted && qItemStatus == 1) {
} /*else if (restricted && qItemStatus == 1) {
continue;
}
} else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) {
}*/
} /*else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) { // thanks Conrad, Legalize for noticing eligible loots not being available to drop for non-killer parties
continue;
}
}*/
return true;
}
@@ -76,19 +76,19 @@ public class MapleLootManager {
return false;
}
public static List<MonsterDropEntry> retrieveRelevantDrops(int monsterId, List<MapleCharacter> partyMembers) {
public static List<MonsterDropEntry> retrieveRelevantDrops(int monsterId, List<MapleCharacter> players) {
List<MonsterDropEntry> loots = MapleMonsterInformationProvider.getInstance().retrieveEffectiveDrop(monsterId);
if(loots.isEmpty()) return loots;
List<MapleLootInventory> partyInv = new LinkedList<>();
for(MapleCharacter chr : partyMembers) {
List<MapleLootInventory> playersInv = new LinkedList<>();
for(MapleCharacter chr : players) {
MapleLootInventory lootInv = new MapleLootInventory(chr);
partyInv.add(lootInv);
playersInv.add(lootInv);
}
List<MonsterDropEntry> effectiveLoot = new LinkedList<>();
for(MonsterDropEntry mde : loots) {
if(isRelevantDrop(mde, partyMembers, partyInv)) {
if(isRelevantDrop(mde, players, playersInv)) {
effectiveLoot.add(mde);
}
}
+18 -3
View File
@@ -85,8 +85,6 @@ import server.life.MonsterDropEntry;
import server.life.MonsterGlobalDropEntry;
import server.life.SpawnPoint;
import scripting.event.EventInstanceManager;
import server.expeditions.MapleExpedition;
import server.expeditions.MapleExpeditionType;
import server.life.MaplePlayerNPC;
import server.life.MonsterListener;
import server.partyquest.GuardianSpawnPoint;
@@ -744,7 +742,7 @@ public class MapleMap {
final List<MonsterDropEntry> dropEntry = new ArrayList<>();
final List<MonsterDropEntry> visibleQuestEntry = new ArrayList<>();
final List<MonsterDropEntry> otherQuestEntry = new ArrayList<>();
sortDropEntries(ServerConstants.USE_SPAWN_RELEVANT_LOOT ? chr.retrieveRelevantDrops(mob.getId()) : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr);
sortDropEntries(ServerConstants.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr);
registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob);
}
@@ -1281,6 +1279,15 @@ public class MapleMap {
return character;
}
public Map<Integer, MapleCharacter> getMapAllPlayers() {
Map<Integer, MapleCharacter> pchars = new HashMap<>();
for (MapleCharacter chr : this.getAllPlayers()) {
pchars.put(chr.getId(), chr);
}
return pchars;
}
public List<MapleCharacter> getPlayersInRange(Rectangle box, List<MapleCharacter> targets) {
List<MapleCharacter> character = new LinkedList<>();
chrRLock.lock();
@@ -3724,6 +3731,14 @@ public class MapleMap {
}
}
public void mobMpRecovery() {
for (MapleMonster mob : this.getAllMonsters()) {
if (mob.isAlive()) {
mob.heal(0, mob.getLevel());
}
}
}
public final int getNumPlayersInArea(final int index) {
return getNumPlayersInRect(getArea(index));
}
+30 -134
View File
@@ -23,23 +23,18 @@ package server.maps;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.PortalFactory;
import server.life.AbstractLoadedMapleLife;
@@ -54,40 +49,17 @@ import tools.StringUtil;
public class MapleMapFactory {
private static Map<Integer, Float> mapRecoveryRate = new HashMap<>();
private MapleDataProvider source;
private MapleData nameData;
private EventInstanceManager event;
private Map<Integer, MapleMap> maps = new HashMap<>();
private ReadLock mapsRLock;
private WriteLock mapsWLock;
private int channel, world;
public MapleMapFactory(EventInstanceManager eim, MapleDataProvider source, MapleDataProvider stringSource, int world, int channel) {
this.source = source;
this.nameData = stringSource.getData("Map.img");
this.world = world;
this.channel = channel;
this.event = eim;
ReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_FACTORY);
this.mapsRLock = rrwl.readLock();
this.mapsWLock = rrwl.writeLock();
private static Map<Integer, Float> mapRecoveryRateCache = new HashMap<>();
private static MapleData nameData;
private static MapleDataProvider mapSource;
static {
nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img");
mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
}
public MapleMap resetMap(int mapid) {
mapsWLock.lock();
try {
maps.remove(Integer.valueOf(mapid));
} finally {
mapsWLock.unlock();
}
return getMap(mapid);
}
private void loadLifeFromWz(MapleMap map, MapleData mapData) {
private static void loadLifeFromWz(MapleMap map, MapleData mapData) {
for (MapleData life : mapData.getChildByPath("life")) {
life.getName();
String id = MapleDataTool.getString(life.getChildByPath("id"));
@@ -115,7 +87,7 @@ public class MapleMapFactory {
}
}
private void loadLifeFromDb(MapleMap map) {
private static void loadLifeFromDb(MapleMap map) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM plife WHERE map = ? and world = ?");
@@ -148,7 +120,7 @@ public class MapleMapFactory {
}
}
private void loadLifeRaw(MapleMap map, int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide, int mobTime, int team) {
private static void loadLifeRaw(MapleMap map, int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide, int mobTime, int team) {
AbstractLoadedMapleLife myLife = loadLife(id, type, cy, f, fh, rx0, rx1, x, y, hide);
if (myLife instanceof MapleMonster) {
MapleMonster monster = (MapleMonster) myLife;
@@ -166,30 +138,17 @@ public class MapleMapFactory {
}
}
private synchronized MapleMap loadMapFromWz(int mapid, Integer omapid, boolean cache) {
public static MapleMap loadMapFromWz(int mapid, int world, int channel, EventInstanceManager event) {
MapleMap map;
if (cache) {
mapsRLock.lock();
try {
map = maps.get(omapid);
} finally {
mapsRLock.unlock();
}
if (map != null) {
return map;
}
}
String mapName = getMapName(mapid);
MapleData mapData = source.getData(mapName); // source.getData issue with giving nulls in rare ocasions found thanks to MedicOP
MapleData mapData = mapSource.getData(mapName); // source.getData issue with giving nulls in rare ocasions found thanks to MedicOP
MapleData infoData = mapData.getChildByPath("info");
String link = MapleDataTool.getString(infoData.getChildByPath("link"), "");
if (!link.equals("")) { //nexon made hundreds of dojo maps so to reduce the size they added links.
mapName = getMapName(Integer.parseInt(link));
mapData = source.getData(mapName);
mapData = mapSource.getData(mapName);
}
float monsterRate = 0;
MapleData mobRate = infoData.getChildByPath("mobRate");
@@ -288,7 +247,7 @@ public class MapleMapFactory {
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ? AND world = ?")) {
ps.setInt(1, omapid);
ps.setInt(1, mapid);
ps.setInt(2, world);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
@@ -352,12 +311,12 @@ public class MapleMapFactory {
}
}
try {
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(omapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(omapid)), ""));
map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), ""));
map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), ""));
} catch (Exception e) {
if (omapid / 1000 != 1020) { // explorer job introducion scenes
if (mapid / 1000 != 1020) { // explorer job introduction scenes
e.printStackTrace();
System.err.println("Not found mapid " + omapid);
System.err.println("Not found mapid " + mapid);
}
map.setMapName("");
@@ -365,8 +324,8 @@ public class MapleMapFactory {
}
map.setClock(mapData.getChildByPath("clock") != null);
map.setEverlast(infoData.getChildByPath("everlast") != null);
map.setTown(infoData.getChildByPath("town") != null);
map.setEverlast(MapleDataTool.getIntConvert("everlast", infoData, 0) != 0); // thanks davidlafriniere for noticing value 0 accounting as true
map.setTown(MapleDataTool.getIntConvert("town", infoData, 0) != 0);
map.setHPDec(MapleDataTool.getIntConvert("decHP", infoData, 0));
map.setHPDecProtect(MapleDataTool.getIntConvert("protectItem", infoData, 0));
map.setForcedReturnMap(MapleDataTool.getInt(infoData.getChildByPath("forcedReturn"), 999999999));
@@ -378,7 +337,7 @@ public class MapleMapFactory {
MapleData recData = infoData.getChildByPath("recovery");
if (recData != null) {
float recoveryRate = MapleDataTool.getFloat(recData);
mapRecoveryRate.put(mapid, recoveryRate);
mapRecoveryRateCache.put(mapid, recoveryRate);
}
HashMap<Integer, Integer> backTypes = new HashMap<>();
@@ -397,46 +356,10 @@ public class MapleMapFactory {
map.setBackgroundTypes(backTypes);
map.generateMapDropRangeCache();
if (cache) {
mapsWLock.lock();
try {
maps.put(omapid, map);
} finally {
mapsWLock.unlock();
}
}
return map;
}
public MapleMap getMap(int mapid) {
Integer omapid = Integer.valueOf(mapid);
MapleMap map;
mapsRLock.lock();
try {
map = maps.get(omapid);
} finally {
mapsRLock.unlock();
}
return (map != null) ? map : loadMapFromWz(mapid, omapid, true);
}
public MapleMap getDisposableMap(int mapid) {
return loadMapFromWz(mapid, mapid, false);
}
public boolean isMapLoaded(int mapId) {
mapsRLock.lock();
try {
return maps.containsKey(mapId);
} finally {
mapsRLock.unlock();
}
}
private AbstractLoadedMapleLife loadLife(int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide) {
private static AbstractLoadedMapleLife loadLife(int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide) {
AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(id, type);
myLife.setCy(cy);
myLife.setF(f);
@@ -450,7 +373,7 @@ public class MapleMapFactory {
return myLife;
}
private MapleReactor loadReactor(MapleData reactor, String id, final byte FacingDirection) {
private static MapleReactor loadReactor(MapleData reactor, String id, final byte FacingDirection) {
MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(Integer.parseInt(id)), Integer.parseInt(id));
int x = MapleDataTool.getInt(reactor.getChildByPath("x"));
int y = MapleDataTool.getInt(reactor.getChildByPath("y"));
@@ -462,7 +385,7 @@ public class MapleMapFactory {
return myReactor;
}
private String getMapName(int mapid) {
private static String getMapName(int mapid) {
String mapName = StringUtil.getLeftPaddedStr(Integer.toString(mapid), '0', 9);
StringBuilder builder = new StringBuilder("Map/Map");
int area = mapid / 100000000;
@@ -474,7 +397,7 @@ public class MapleMapFactory {
return mapName;
}
private String getMapStringName(int mapid) {
private static String getMapStringName(int mapid) {
StringBuilder builder = new StringBuilder();
if (mapid < 100000000) {
builder.append("maple");
@@ -513,35 +436,8 @@ public class MapleMapFactory {
return builder.toString();
}
public void setChannel(int channel) {
this.channel = channel;
}
public void setWorld(int world) {
this.channel = world;
}
public Map<Integer, MapleMap> getMaps() {
mapsRLock.lock();
try {
return new HashMap<>(maps);
} finally {
mapsRLock.unlock();
}
}
public void dispose() {
Collection<MapleMap> mapValues = getMaps().values();
for (MapleMap map : mapValues) {
map.dispose();
}
this.event = null;
}
public static float getMapRecoveryRate(int mapid) {
Float recRate = mapRecoveryRate.get(mapid);
Float recRate = mapRecoveryRateCache.get(mapid);
return recRate != null ? recRate : 1.0f;
}
}
+162
View File
@@ -0,0 +1,162 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.maps;
import constants.ServerConstants;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import scripting.event.EventInstanceManager;
import server.TimerManager;
public class MapleMapManager {
private int channel, world;
private EventInstanceManager event;
private Map<Integer, MapleMap> maps = new HashMap<>();
private ScheduledFuture<?> updateTask;
private ReadLock mapsRLock;
private WriteLock mapsWLock;
public MapleMapManager(EventInstanceManager eim, int world, int channel) {
this.world = world;
this.channel = channel;
this.event = eim;
ReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER);
this.mapsRLock = rrwl.readLock();
this.mapsWLock = rrwl.writeLock();
updateTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
updateMaps();
}
}, ServerConstants.RESPAWN_INTERVAL);
}
public MapleMap resetMap(int mapid) {
mapsWLock.lock();
try {
maps.remove(mapid);
} finally {
mapsWLock.unlock();
}
return getMap(mapid);
}
private synchronized MapleMap loadMapFromWz(int mapid, boolean cache) {
MapleMap map;
if (cache) {
mapsRLock.lock();
try {
map = maps.get(mapid);
} finally {
mapsRLock.unlock();
}
if (map != null) {
return map;
}
}
map = MapleMapFactory.loadMapFromWz(mapid, world, channel, event);
if (cache) {
mapsWLock.lock();
try {
maps.put(mapid, map);
} finally {
mapsWLock.unlock();
}
}
return map;
}
public MapleMap getMap(int mapid) {
MapleMap map;
mapsRLock.lock();
try {
map = maps.get(mapid);
} finally {
mapsRLock.unlock();
}
return (map != null) ? map : loadMapFromWz(mapid, true);
}
public MapleMap getDisposableMap(int mapid) {
return loadMapFromWz(mapid, false);
}
public boolean isMapLoaded(int mapId) {
mapsRLock.lock();
try {
return maps.containsKey(mapId);
} finally {
mapsRLock.unlock();
}
}
public Map<Integer, MapleMap> getMaps() {
mapsRLock.lock();
try {
return new HashMap<>(maps);
} finally {
mapsRLock.unlock();
}
}
private void updateMaps() {
for (MapleMap map : getMaps().values()) {
map.respawn();
map.mobMpRecovery();
}
}
public void dispose() {
if (updateTask != null) {
updateTask.cancel(false);
updateTask = null;
}
for (MapleMap map : getMaps().values()) {
map.dispose();
}
this.event = null;
}
public static float getMapRecoveryRate(int mapid) {
return MapleMapFactory.getMapRecoveryRate(mapid);
}
}
+3 -4
View File
@@ -194,7 +194,7 @@ public class MapleReactor extends AbstractMapleMapObject {
}
private void tryForceHitReactor(final byte newState) { // weak hit state signal, if already changed reactor state before timeout then drop this
if (!this.reactorLock.tryLock()) {
if (!reactorLock.tryLock()) {
return;
}
@@ -202,7 +202,7 @@ public class MapleReactor extends AbstractMapleMapObject {
this.resetReactorActions(newState);
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, (short) 0));
} finally {
this.reactorLock.unlock();
reactorLock.unlock();
}
}
@@ -311,9 +311,8 @@ public class MapleReactor extends AbstractMapleMapObject {
}
} finally {
this.unlockReactor();
hitLock.unlock(); // non-encapsulated unlock found thanks to MiLin
}
hitLock.unlock();
}
} catch (Exception e) {
e.printStackTrace();