diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt
index 285c1bb371..73a827c003 100644
--- a/docs/mychanges_ptbr.txt
+++ b/docs/mychanges_ptbr.txt
@@ -1093,4 +1093,18 @@ Refatorado MonitoredLockTypes para agora dar um label específico a cada lock do
29 - 30 Junho 2018,
Corrigido quest Milk Jug com NPCs trocados.
Adicionado um delay na aplicação de efeitos dos skills de mobs, buffs e demais efeitos agora são registrados após o tempo da animação.
-Corrigido Flame Thrower atuando passivamente quando o jogador usa uma skill de ataque.
\ No newline at end of file
+Corrigido Flame Thrower atuando passivamente quando o jogador usa uma skill de ataque.
+
+01 Julho 2018,
+Implementado um sistema abstrato de temporizador para channels (otimizado para rodar numa única thread). A ideia é que quaisquer temporizadores a ser instalado num canal use uma extensão dessa classe.
+Implementado sistema de detecção de animação-em-andamento de mobs, buscando evitar assim spam de uso de habilidades pelos mobs.
+Server agora verifica se o mob está com alguma animação sendo rodada e baseado nisso informa ao cliente se o mob pode ou não enviar requisições de mob skill a ser ativada juntamente ao MoveLifeHandler.
+
+02 Julho 2018,
+Melhorado proteção contra acesso concorrente nos registros de listeners de MapleMonster.
+Melhorado liberação de recursos ao finalizar objeto MapleMonster.
+
+03 - 04 Julho 2018,
+Adicionado server flag que permite usar clean slates mesmo em equipamentos sem slots vazios (onde falhou o scroll).
+Corrigido um possível ponto crítico de deadlock com MapleServerHandler.
+Adicionado portal SFX para vários portais scriptados que ainda faltavam o efeito.
\ No newline at end of file
diff --git a/nbproject/project.properties b/nbproject/project.properties
index 1d9f2418ed..b250ad8009 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -93,7 +93,7 @@ run.classpath=\
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
-run.jvmargs=
+run.jvmargs=-Xmx2048m -Dwzpath=wz/
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
diff --git a/scripts/npc/9270043.js b/scripts/npc/9270043.js
index 253e9152cc..35eb969995 100644
--- a/scripts/npc/9270043.js
+++ b/scripts/npc/9270043.js
@@ -19,7 +19,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-//Gachaphon
+//Gachapon
var ids = [2000004,2020012,2000005,2030007,2022027,2040001,2041002, 2040805, 2040702, 2043802, 2040402, 2043702, 1302022, 1322021, 1322026, 1302026, 1442017, 1082147, 1102043, 1442016, 1402012, 1302027, 1322027, 1322025, 1312012, 1062000, 1332020, 1302028, 1372002, 1002033, 1092022, 1302021, 1102041, 1102042, 1322024, 1082148, 1002012, 1322012, 1322022, 1002020, 1302013, 1082146, 1442014, 1002096, 1302017, 1442012, 1322010, 1442011, 1442018, 1092011, 1092014, 1302003, 1432001, 1312011, 1002088, 1041020, 1322015, 1442004, 1422008, 1302056, 1432000, 1382001, 1041053, 1060014, 1050053, 1051032, 1050073, 1061036, 1002253, 1002034, 1051025, 1050067, 1051052, 1002072, 1002144, 1051054, 1050069, 1372007, 1050056, 1050074, 1002254, 1002274, 1002218, 1051055, 1382010, 1002246, 1050039, 1382007, 1372000, 1002013, 1050072, 1002036, 1002243, 1372008, 1382008, 1382011, 1092021, 1051034, 1050047, 1040019, 1041031, 1051033, 1002153, 1002252, 1051024, 1002153, 1050068, 1382003, 1382006, 1050055, 1051031, 1050025, 1002155, 1002245, 1452004, 1452023, 1060057, 1040071, 1002137, 1462009, 1452017, 1040025, 1041027, 1452005, 1452007, 1061057, 1472006, 1472019, 1060084, 1472028, 1002179, 1082074, 1332015, 1432001, 1060071, 1472007, 1472002, 1051009, 1061037, 1332016, 1332034, 1472020, 1102084, 1102086, 1102042, 1032026, 1082149];
var status = 0;
diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js
index b35a45ff68..f7baa36cbd 100644
--- a/scripts/npc/9977777.js
+++ b/scripts/npc/9977777.js
@@ -116,6 +116,7 @@ function writeFeatureTab_MonstersMapsReactors() {
addFeature("C. Balrog's boat approaching visual effect functional.");
addFeature("Maps having everlasting items no longer expires them.");
addFeature("PQs, Taxis and events warps players to random SPs.");
+ addFeature("Uncovered missing portal SFX on scripted portals.");
addFeature("PQ boxes sprays items when opened, GMS-like.");
addFeature("Reactors pick items up smartly from the field.");
addFeature("Reviewed Masteria, W. Tour, N. Desert and Neo City.");
@@ -199,8 +200,9 @@ function writeFeatureTab_Project() {
addFeature("Reviewed many Java aspects that needed attention.");
addFeature("Reviewed SQL data, eliminating duplicated entries.");
addFeature("Protected many flaws with login management system.");
- addFeature("Developed many survey tools for content management.");
+ addFeature("Developed many survey tools for content profiling.");
addFeature("ThreadTracker: runtime tool for deadlock detection.");
+ addFeature("Channel, World and Server-wide timer management.");
addFeature("Heavily reviewed future task management, spawning much less threads and relieving task overload on the TimerManager.");
}
@@ -240,7 +242,7 @@ function action(mode, type, selection) {
status--;
if (status == 0) {
- var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. On the meantime many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features of #bHeavenMS#k:\r\n\r\n";
+ var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. On the meantime many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n";
for(var i = 0; i < tabs.length; i++) {
sendStr += "#L" + i + "##b" + tabs[i] + "#k#l\r\n";
}
diff --git a/scripts/portal/hontale_Bopen.js b/scripts/portal/hontale_Bopen.js
index d8577d9643..2ca2ce3b7e 100644
--- a/scripts/portal/hontale_Bopen.js
+++ b/scripts/portal/hontale_Bopen.js
@@ -40,7 +40,8 @@ function enter(pi) {
// do nothing; send message to player
pi.getPlayer().dropMessage(6, "Horntail\'s Seal is Blocking this Door.");
return false;
- }else {
+ } else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
@@ -56,7 +57,8 @@ function enter(pi) {
// do nothing; send message to player
pi.getPlayer().dropMessage(6, "Horntail\'s Seal is Blocking this Door.");
return false;
- }else {
+ } else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
@@ -72,7 +74,8 @@ function enter(pi) {
// do nothing; send message to player
pi.getPlayer().dropMessage(6, "Horntail\'s Seal is Blocking this Door.");
return false;
- }else {
+ } else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
@@ -88,7 +91,8 @@ function enter(pi) {
// do nothing; send message to player
pi.getPlayer().dropMessage(6, "Horntail\'s Seal is Blocking this Door.");
return false;
- }else {
+ } else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
@@ -104,6 +108,7 @@ function enter(pi) {
if (pi.haveItem(4001092) && pi.isEventLeader()) {
eim.showClearEffect();
pi.getPlayer().dropMessage(6, "The leader's key break the seal for a flash...");
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
eim.setIntProperty("5stageclear", 1);
return true;
@@ -111,7 +116,8 @@ function enter(pi) {
pi.getPlayer().dropMessage(6, "Horntail\'s Seal is blocking this door. Only the leader with the key can lift this seal.");
return false;
}
- }else {
+ } else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/hontale_C.js b/scripts/portal/hontale_C.js
index e0fa7ee5f3..3f6f08795b 100644
--- a/scripts/portal/hontale_C.js
+++ b/scripts/portal/hontale_C.js
@@ -39,6 +39,7 @@ function enter(pi) {
return false;
}
+ pi.playPortalSound();
eim.warpEventTeam(target);
return true;
} else {
diff --git a/scripts/portal/kpq0.js b/scripts/portal/kpq0.js
index 313ce2bdf5..18112930d3 100644
--- a/scripts/portal/kpq0.js
+++ b/scripts/portal/kpq0.js
@@ -28,6 +28,7 @@ function enter(pi) {
var target = eim.getMapInstance(103000801);
if (eim.getProperty("1stageclear") != null) {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, target.getPortal("st00"));
return true;
}
diff --git a/scripts/portal/kpq1.js b/scripts/portal/kpq1.js
index 7b27dcb2d5..b41259baf6 100644
--- a/scripts/portal/kpq1.js
+++ b/scripts/portal/kpq1.js
@@ -28,6 +28,7 @@ function enter(pi) {
var eim = pi.getPlayer().getEventInstance();
var target = eim.getMapInstance(103000802);
if (eim.getProperty("2stageclear") != null) {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, target.getPortal("st00"));
return true;
}
diff --git a/scripts/portal/kpq2.js b/scripts/portal/kpq2.js
index 713229218b..9e95c3bfb7 100644
--- a/scripts/portal/kpq2.js
+++ b/scripts/portal/kpq2.js
@@ -27,6 +27,7 @@ function enter(pi) {
var eim = pi.getPlayer().getEventInstance();
var target = eim.getMapInstance(103000803);
if (eim.getProperty("3stageclear") != null) {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, target.getPortal("st00"));
return true;
}
diff --git a/scripts/portal/kpq3.js b/scripts/portal/kpq3.js
index 838e80725d..9a57b83573 100644
--- a/scripts/portal/kpq3.js
+++ b/scripts/portal/kpq3.js
@@ -28,6 +28,7 @@ function enter(pi) {
var eim = pi.getPlayer().getEventInstance();
var target = eim.getMapInstance(103000804);
if (eim.getProperty("4stageclear") != null) {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, target.getPortal("st00"));
return true;
}
diff --git a/scripts/portal/kpq4.js b/scripts/portal/kpq4.js
index cc2f378dc6..940f3c388d 100644
--- a/scripts/portal/kpq4.js
+++ b/scripts/portal/kpq4.js
@@ -27,6 +27,7 @@ function enter(pi) {
var eim = pi.getPlayer().getEventInstance();
var target = eim.getMapInstance(103000805);
if (eim.getProperty("5stageclear") != null) {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, target.getPortal("st00"));
return true;
}
diff --git a/scripts/portal/lpq0.js b/scripts/portal/lpq0.js
index d93479428b..1d0ee1c6e2 100644
--- a/scripts/portal/lpq0.js
+++ b/scripts/portal/lpq0.js
@@ -35,6 +35,7 @@ function enter(pi) {
return false;
}
else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/lpq1.js b/scripts/portal/lpq1.js
index 113139a4ac..3ad14c4a3d 100644
--- a/scripts/portal/lpq1.js
+++ b/scripts/portal/lpq1.js
@@ -35,6 +35,7 @@ function enter(pi) {
return false;
}
else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/lpq2.js b/scripts/portal/lpq2.js
index fea16934d8..7833a35095 100644
--- a/scripts/portal/lpq2.js
+++ b/scripts/portal/lpq2.js
@@ -36,6 +36,7 @@ function enter(pi) {
return false;
}
else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/lpq3.js b/scripts/portal/lpq3.js
index 728fa61e1a..7182a41bba 100644
--- a/scripts/portal/lpq3.js
+++ b/scripts/portal/lpq3.js
@@ -36,6 +36,7 @@ function enter(pi) {
return false;
}
else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/lpq4.js b/scripts/portal/lpq4.js
index d6323e4712..3ac0e0256e 100644
--- a/scripts/portal/lpq4.js
+++ b/scripts/portal/lpq4.js
@@ -36,6 +36,7 @@ function enter(pi) {
return false;
}
else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/lpq5.js b/scripts/portal/lpq5.js
index 961cef852f..2320535d2c 100644
--- a/scripts/portal/lpq5.js
+++ b/scripts/portal/lpq5.js
@@ -40,6 +40,7 @@ function enter(pi) {
if(eim.getProperty("6stageclear") == null) {
eim.setProperty("6stageclear", "true");
}
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/lpq6.js b/scripts/portal/lpq6.js
index 9aa193d9de..0d80d7a111 100644
--- a/scripts/portal/lpq6.js
+++ b/scripts/portal/lpq6.js
@@ -27,6 +27,7 @@ function enter(pi) {
var eim = pi.getPlayer().getEventInstance();
var target = eim.getMapInstance(922010800);
if (eim.getProperty("7stageclear") != null) {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, target.getPortal("st00"));
return true;
} else
diff --git a/scripts/portal/lpq7.js b/scripts/portal/lpq7.js
index 05221f2f28..4d37be40c2 100644
--- a/scripts/portal/lpq7.js
+++ b/scripts/portal/lpq7.js
@@ -37,6 +37,7 @@ function enter(pi) {
return false;
}
else {
+ pi.playPortalSound();
pi.getPlayer().changeMap(target, targetPortal);
return true;
}
diff --git a/scripts/portal/party3_gardenin.js b/scripts/portal/party3_gardenin.js
index 4ab499afd0..1f2148a2ea 100644
--- a/scripts/portal/party3_gardenin.js
+++ b/scripts/portal/party3_gardenin.js
@@ -1,5 +1,6 @@
function enter(pi) {
if (pi.getPlayer().getParty() != null && pi.isEventLeader() && pi.haveItem(4001055,1)) {
+ pi.playPortalSound();
pi.getEventInstance().warpEventTeam(920010100);
return true;
} else {
diff --git a/scripts/portal/party6_out.js b/scripts/portal/party6_out.js
index a6c1e90217..cd0ca2aef5 100644
--- a/scripts/portal/party6_out.js
+++ b/scripts/portal/party6_out.js
@@ -3,6 +3,7 @@ function enter(pi) {
if (eim.isEventCleared()) {
if(pi.isEventLeader()) {
+ pi.playPortalSound();
eim.warpEventTeam(930000800);
return true;
} else {
diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java
index ad68592292..c3cbd70cda 100644
--- a/src/constants/ServerConstants.java
+++ b/src/constants/ServerConstants.java
@@ -133,12 +133,13 @@ public class ServerConstants {
public static final boolean USE_PERFECT_SCROLLING = true; //Scrolls doesn't use slots upon failure.
public static final boolean USE_ENHANCED_CHSCROLL = true; //Equips even more powerful with chaos upgrade.
public static final boolean USE_ENHANCED_CRAFTING = true; //Apply chaos scroll on every equip crafted.
+ public static final boolean USE_ENHANCED_CLNSLATE = true; //Clean slates can be applied to recover successfully used slots as well.
public static final int SCROLL_CHANCE_RATE = 10; //Number of rolls for success on a scroll, set 0 for default.
public static final int CHSCROLL_STAT_RATE = 3; //Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default.
public static final int CHSCROLL_STAT_RANGE = 6; //Stat upgrade range (-N, N) on chaos scrolls.
//Beginner Skills Configuration
- public static final boolean USE_ULTRA_NIMBLE_FEET = true; //Haste-like speed & jump upgrade.
+ public static final boolean USE_ULTRA_NIMBLE_FEET = true; //Massive speed & jump upgrade.
public static final boolean USE_ULTRA_RECOVERY = true; //Massive recovery amounts overtime.
public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss.
diff --git a/src/net/MapleServerHandler.java b/src/net/MapleServerHandler.java
index 0217b9ef07..b8ffcac688 100644
--- a/src/net/MapleServerHandler.java
+++ b/src/net/MapleServerHandler.java
@@ -189,10 +189,12 @@ public class MapleServerHandler extends IoHandlerAdapter {
private void registerIdleSession(MapleClient c) {
if(idleLock.tryLock()) {
- idleSessions.put(c, System.currentTimeMillis());
- c.announce(MaplePacketCreator.getPing());
-
- idleLock.unlock();
+ try {
+ idleSessions.put(c, System.currentTimeMillis());
+ c.announce(MaplePacketCreator.getPing());
+ } finally {
+ idleLock.unlock();
+ }
} else {
tempLock.lock();
try {
diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java
index ad43f71bd6..9ae7c78f5b 100644
--- a/src/net/server/channel/Channel.java
+++ b/src/net/server/channel/Channel.java
@@ -21,7 +21,6 @@ along with this program. If not, see .
*/
package net.server.channel;
-import net.server.channel.worker.MobStatusScheduler;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.ArrayList;
@@ -43,9 +42,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
-import net.server.Server;
-import net.server.world.World;
+
import net.server.PlayerStorage;
+import net.server.Server;
+import net.server.channel.worker.*;
+
+import net.server.world.World;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
@@ -86,6 +88,8 @@ public final class Channel {
private MapleMapFactory mapFactory;
private EventScriptManager eventSM;
private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[4];
+ private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[4];
+ private OverallScheduler channelSchedulers[] = new OverallScheduler[4];
private Map hiredMerchants = new HashMap<>();
private final Map storedVars = new HashMap<>();
private List expeditions = new ArrayList<>();
@@ -154,6 +158,8 @@ public final class Channel {
for(int i = 0; i < 4; i++) {
mobStatusSchedulers[i] = new MobStatusScheduler();
+ mobAnimationSchedulers[i] = new MobAnimationScheduler();
+ channelSchedulers[i] = new OverallScheduler();
}
System.out.println(" Channel " + getId() + ": Listening on port " + port);
@@ -824,7 +830,7 @@ public final class Channel {
}
}
- private static int getMobStatusSchedulerIndex(int mapid) {
+ private static int getChannelSchedulerIndex(int mapid) {
if(mapid >= 250000000) {
if(mapid >= 900000000) {
return 3;
@@ -845,11 +851,23 @@ public final class Channel {
}
public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) {
- mobStatusSchedulers[getMobStatusSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay);
+ mobStatusSchedulers[getChannelSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay);
}
public void interruptMobStatus(int mapid, MonsterStatusEffect mse) {
- mobStatusSchedulers[getMobStatusSchedulerIndex(mapid)].interruptMobStatus(mse);
+ mobStatusSchedulers[getChannelSchedulerIndex(mapid)].interruptMobStatus(mse);
+ }
+
+ public boolean registerMobOnAnimationEffect(int mapid, int mobHash, long delay) {
+ return mobAnimationSchedulers[getChannelSchedulerIndex(mapid)].registerAnimationMode(mobHash, delay);
+ }
+
+ public void registerOverallAction(int mapid, Runnable runAction, long delay) {
+ channelSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay);
+ }
+
+ public void forceRunOverallAction(int mapid, Runnable runAction) {
+ channelSchedulers[getChannelSchedulerIndex(mapid)].forceRunDelayedAction(runAction);
}
public void debugMarriageStatus() {
diff --git a/src/net/server/channel/handlers/MoveLifeHandler.java b/src/net/server/channel/handlers/MoveLifeHandler.java
index 6223b19f31..8173258691 100644
--- a/src/net/server/channel/handlers/MoveLifeHandler.java
+++ b/src/net/server/channel/handlers/MoveLifeHandler.java
@@ -43,6 +43,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
/**
* @author Danny (Leifde)
* @author ExtremeDevilz
+ * @author Ronan (HeavenMS)
*/
public final class MoveLifeHandler extends AbstractMovementPacketHandler {
@Override
@@ -76,49 +77,62 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
int nextCastSkill = useSkillId;
int nextCastSkillLevel = useSkillLevel;
-
- MobSkill toUse = null;
-
- int percHpLeft = (int) (((float) monster.getHp() / monster.getMaxHp()) * 100);
- if (nextMovementCouldBeSkill && monster.getNoSkills() > 0) {
- int Random = Randomizer.nextInt(monster.getNoSkills());
- Pair skillToUse = monster.getSkills().get(Random);
- nextCastSkill = skillToUse.getLeft();
- nextCastSkillLevel = skillToUse.getRight();
- toUse = MobSkillFactory.getMobSkill(nextCastSkill, nextCastSkillLevel);
+ MobSkill toUse = null;
+ int rndSkill = -1;
+
+ if(monster.getNoSkills() > 0) {
+ if(nextMovementCouldBeSkill) {
+ rndSkill = Randomizer.nextInt(monster.getNoSkills());
+ }
+ } else {
+ nextMovementCouldBeSkill = false;
+ }
+
+ if(monster.applyAnimationIfRoaming((attackId - 13) / 2, rndSkill)) {
+ if (rndSkill > -1) {
+ Pair skillToUse = monster.getSkills().get(rndSkill);
+ nextCastSkill = skillToUse.getLeft();
+ nextCastSkillLevel = skillToUse.getRight();
+ toUse = MobSkillFactory.getMobSkill(nextCastSkill, nextCastSkillLevel);
- if (isSkill || isAttack) {
- if (nextCastSkill != toUse.getSkillId() || nextCastSkillLevel != toUse.getSkillLevel()) {
- //toUse.resetAnticipatedSkill();
- return;
- } else if (toUse.getHP() < percHpLeft) {
- toUse = null;
- } else if (monster.canUseSkill(toUse)) {
- int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(monster.getId(), Random);
- if(animationTime > 0) {
- toUse.applyDelayedEffect(c.getPlayer(), monster, true, banishPlayers, animationTime);
+ if (isSkill || isAttack) {
+ int percHpLeft = (int) (((float) monster.getHp() / monster.getMaxHp()) * 100);
+ if (nextCastSkill != toUse.getSkillId() || nextCastSkillLevel != toUse.getSkillLevel()) {
+ //toUse.resetAnticipatedSkill();
+ return;
+ } else if (toUse.getHP() < percHpLeft) {
+ toUse = null;
+ } else if (monster.canUseSkill(toUse)) {
+ int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(monster.getId(), rndSkill);
+ if(animationTime > 0) {
+ toUse.applyDelayedEffect(c.getPlayer(), monster, true, banishPlayers, animationTime);
+ } else {
+ toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers);
+ }
} else {
- toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers);
+ toUse = null;
}
- } else {
- toUse = null;
- }
- } else {
- toUse = null; // paliative measure for suspicious mob movement
-
- /*
- long curtime = System.currentTimeMillis();
- if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3
- MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
- monster.setNextBasicSkillTime(curtime);
} else {
- toUse = null;
- }
- */
- }
- }
+ toUse = null; // paliative measure for suspicious mob movement
+ /*
+ long curtime = System.currentTimeMillis();
+ if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3
+ MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
+ monster.setNextBasicSkillTime(curtime);
+ } else {
+ toUse = null;
+ }
+ */
+ }
+ }
+ } else {
+ if(rndSkill > -1) {
+ nextMovementCouldBeSkill = false;
+ }
+ }
+
slea.readByte();
slea.readInt(); // whatever
short start_x = slea.readShort(); // hmm.. startpos?
diff --git a/src/net/server/channel/handlers/ScrollHandler.java b/src/net/server/channel/handlers/ScrollHandler.java
index 2605330923..b246fb9f6a 100644
--- a/src/net/server/channel/handlers/ScrollHandler.java
+++ b/src/net/server/channel/handlers/ScrollHandler.java
@@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.List;
import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator;
+import constants.ServerConstants;
import server.MapleItemInformationProvider;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -91,9 +92,10 @@ public final class ScrollHandler extends AbstractMaplePacketHandler {
}
}
- if (ItemConstants.isCleanSlate(scroll.getItemId()) && !(toScroll.getLevel() + toScroll.getUpgradeSlots() < ii.getEquipStats(toScroll.getItemId()).get("tuc"))) { //upgrade slots can be over because of hammers
+ if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) {
return;
}
+
Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, scroll.getItemId(), whiteScroll, 0, c.getPlayer().isGM());
ScrollResult scrollSuccess = Equip.ScrollResult.FAIL; // fail
if (scrolled == null) {
diff --git a/src/net/server/channel/worker/BaseScheduler.java b/src/net/server/channel/worker/BaseScheduler.java
new file mode 100644
index 0000000000..9a1f88ba5b
--- /dev/null
+++ b/src/net/server/channel/worker/BaseScheduler.java
@@ -0,0 +1,131 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft 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 .
+*/
+package net.server.channel.worker;
+
+import constants.ServerConstants;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.locks.Lock;
+import server.TimerManager;
+import tools.Pair;
+import tools.locks.MonitoredLockType;
+import tools.locks.MonitoredReentrantLock;
+
+/**
+ *
+ * @author Ronan
+ */
+public abstract class BaseScheduler {
+ private int idleProcs = 0;
+ private List listeners = new LinkedList<>();
+ private Map