- Joined
- Jul 17, 2010
- Messages
- 333
- Reaction score
- 165
It seems that most released servers don't use proper delay for mobskill, so I'll provide a little information I know.
*Attention* I am no good at English so my sentences may be terrible.
First, check if the mobskill effect(buff,debuff,spawn,etc...) is not applied before the actual motion.
in MobHandler(MOVE_LIFE)
this should be
The applyEffect has to be applied to 'skillID/skillLv' rather than 'realskill/level'.
(mob movement's flow chart)
This 'option' mean duration of skill motion (when it is attack motion, it'll be used for shootAttack or something idk.)
Then, add this 'option' (effectDelay) to codes (monster buff, player debuff, spawn, mist) through the applyEffect. (I'll omit explanation of this)
in giveDebuff(v117)
in applyMonsterStatus(v117)
for already buffed
for damage reflect
...but buff/debuff's effect delay is applied only to the effect visually, so please do not expect. (e.g. Pinkbean or Cygnus's DR)
in spawnMonster
in spawnMist
--Edit--
sample video
----
After Von Leon released(?), they started using 'skillAfter' for general skills.
Server sends MobSkillDelay (contains oid, sid, slv, delay, option), and after a specified delay, will receive MobSkillDelayEnd (contains oid, sid, slv, option) from Client.
This is the difference between Pinkbean, Cygnus's DR and Vonleon, Arkarium's DR.
[Send] MobSkillDelay(v95+?) : Opcode is before MONSTER_PROPERTIES? or around it
(for older version)
(for newer version *not checked)
(for latest version)
[Recv] MobSkillDelayEnd(v95+?) : Opcode is between HYPNOTIZE_DMG and MOB_BOMB? or around them
(for older version)
(for newer version *not checked)
(for latest version)
Then, make MobSkillInfo for each mob's skills (like MobAttackInfo). (omitted agein.)
Finally, edit your MobHandler so that it applies effectdelay properly.
(this is just an example)
(old ver.)
(new ver. for v160+)
now you have the proper effect delay of mobskills.
Thank you for reading
*Attention* I am no good at English so my sentences may be terrible.
First, check if the mobskill effect(buff,debuff,spawn,etc...) is not applied before the actual motion.
in MobHandler(MOVE_LIFE)
PHP:
int unk = slea.readInt();
PHP:
int skillID = slea.readByte() & 0xFF;//skill_1
int skillLv = slea.readByte() & 0xFF;//skill_2
short option = slea.readShort();//skill_3,skill_4
(mob movement's flow chart)
Code:
[R]:Controller's client sends. [S]:Server sends.
//monster spawned
[S] SPAWN_MONSTER //for all players in the map
[S] SPAWN_MONSTER_CONTROLLER //for one player=controller
↓
[R] MOVE_LIFE:1 //current movement
↓
[S] MOVE_MONSTER_RESPONSE:1 //request next movement; enable attacks or a specified skill/(attack)
[S] MOVE_MONSTER:1 //current movement, for all players except the controller
↓
[R] MOVE_LIFE:2
↓
[S] MOVE_MONSTER_RESPONSE:2
[S] MOVE_MONSTER:2
↓
...
Then, add this 'option' (effectDelay) to codes (monster buff, player debuff, spawn, mist) through the applyEffect. (I'll omit explanation of this)
in giveDebuff(v117)
PHP:
mplew.writeInt(duration);
mplew.writeZeroBytes(3);//idk
mplew.writeShort(effectDelay);
PHP:
mplew.writeShort(skil.getSkillLevel());
mplew.writeShort((int) (skil.getDuration() / 500));////duration
mplew.writeShort(effectDelay);
PHP:
if (ms.isMonsterSkill()) {
mplew.writeShort(ms.getMobSkill().getSkillId());
mplew.writeShort(ms.getMobSkill().getSkillLevel());
} else if (ms.getSkill() > 0) {
mplew.writeInt(ms.getSkill());
}
mplew.writeShort((int) ((ms.getCancelTask() - System.currentTimeMillis()) / 500));//duration
mplew.writeShort(0);
PHP:
mplew.writeShort(skil.getSkillLevel());
mplew.writeShort((int) (skil.getDuration() / 500));////duration
}
for (Integer ref : reflection) {
mplew.writeInt(ref);
}
mplew.writeInt(0);
mplew.writeInt(0);
mplew.writeShort(effectDelay);
in spawnMonster
PHP:
mplew.write(summonType);//0+:summon effect, -2:fade in, -3:after remove link mob, -4:fake
if ((summonType == -3) || (summonType >= 0)) {
mplew.writeInt(summonOption);//summonType= -3:link mob oid, 0+:effect delay
}
in spawnMist
PHP:
mplew.write(mist.getSkillLevel());
mplew.writeShort(mist.getSkillDelay());//effectDelay / 100 (e.g. 3240ms -> 32)
sample video
----
After Von Leon released(?), they started using 'skillAfter' for general skills.
Server sends MobSkillDelay (contains oid, sid, slv, delay, option), and after a specified delay, will receive MobSkillDelayEnd (contains oid, sid, slv, option) from Client.
This is the difference between Pinkbean, Cygnus's DR and Vonleon, Arkarium's DR.
[Send] MobSkillDelay(v95+?) : Opcode is before MONSTER_PROPERTIES? or around it
(for older version)
PHP:
public static byte[] MobSkillDelay(int objectId, int skillID, int skillLv, int skillAfter, int option) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendPacketOpcode.MOB_SKILL_DELAY.getValue());
mplew.writeInt(objectId);
mplew.writeInt(skillAfter);
mplew.writeInt(skillID);
mplew.writeInt(skillLv);
mplew.writeInt(option);
return mplew.getPacket();
}
PHP:
public static byte[] MobSkillDelay(int objectId, int skillID, int skillLv, int skillAfter) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendPacketOpcode.MOB_SKILL_DELAY.getValue());
mplew.writeInt(objectId);
mplew.writeInt(skillAfter);
mplew.writeInt(skillID);
mplew.writeInt(skillLv);
mplew.writeInt(0);//rect size
return mplew.getPacket();
}
PHP:
public static byte[] MobSkillDelay(int objectId, int skillID, int skillLv, int skillAfter, short sequenceDelay, List<Rectangle> skillRectInfo) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendPacketOpcode.MOB_SKILL_DELAY.getValue());
mplew.writeInt(objectId);
mplew.writeInt(skillAfter);
mplew.writeInt(skillID);
mplew.writeInt(skillLv);
if (skillRectInfo== null || skillRectInfo.isEmpty()) {
mplew.writeInt(0);
mplew.writeInt(0);
} else {
mplew.writeInt(sequenceDelay);
mplew.writeInt(skillRectInfo.size());
for (Rectangle rect : skillRectInfo) {
mplew.writeInt(rect.x);
mplew.writeInt(rect.y);
mplew.writeInt(rect.x + rect.width);
mplew.writeInt(rect.y + rect.height);
}
}
return mplew.getPacket();
}
[Recv] MobSkillDelayEnd(v95+?) : Opcode is between HYPNOTIZE_DMG and MOB_BOMB? or around them
(for older version)
PHP:
public static final void MobSkillDelayEnd(LittleEndianAccessor slea, MapleCharacter chr) {
MapleMonster monster = chr.getMap().getMonsterByOid(slea.readInt());
if (monster != null) {
int skillID = slea.readInt();
int skillLv = slea.readInt();
int option = slea.readInt();
if (monster.hasSkill(skillID, skillLv)) {
MobSkillFactory.getMobSkill(skillID, skillLv).applyEffect(chr, monster, true, (short) option);
}
}
}
PHP:
public static final void MobSkillDelayEnd(LittleEndianAccessor slea, MapleCharacter chr) {
MapleMonster monster = chr.getMap().getMonsterByOid(slea.readInt());
if (monster != null) {
int skillID = slea.readInt();
int skillLv = slea.readInt();
if (monster.hasSkill(skillID, skillLv)) {
MobSkillFactory.getMobSkill(skillID, skillLv).applyEffect(chr, monster, true);
}
}
}
PHP:
public static final void MobSkillDelayEnd(LittleEndianAccessor slea, MapleCharacter chr) {
MapleMonster monster = chr.getMap().getMonsterByOid(slea.readInt());
if (monster != null) {
int skillID = slea.readInt();
int skillLv = slea.readInt();
short remainCount = 0;
if (slea.readBool()) {
remainCount = (short) slea.readInt();
}
if (skillID == monster.getSkillCommand() && skillLv == monster.getSLV()) {
MobSkillFactory.getMobSkill(skillID, skillLv).applyEffect(c.getPlayer(), monster, remainCount);
if (remainCount <= 1) {
monster.resetSkillCommand();
}
}
}
}
Then, make MobSkillInfo for each mob's skills (like MobAttackInfo). (omitted agein.)
Finally, edit your MobHandler so that it applies effectdelay properly.
(this is just an example)
(old ver.)
PHP:
final int nAction = skill >> 1;
final int skillIdx = nAction - 22;//at v117
~~~
if (skillIdx >= 0 && skillIdx <= 10) {//at v117
MobSkillInfo msi = monster.getStats().getMobSkill(skillIdx);
if (msi != null) {
if (msi.getSkillAfter() > 0) {//Note:should except Teleport (instead, send Teleport packet)
c.getSession().write(MobPacket.MobSkillDelay(oid, skillID, skillLv, msi.getSkillAfter()/*, msi.getEffectAfter()*/));
} else if (msi.getEffectAfter() <= 0) {
msi.getMobSkill().applyEffect(chr, monster, true, option);
} else {//idk
msi.getMobSkill().applyEffect(chr, monster, true, msi.getEffectAfter());
}
}
}
PHP:
final int nAction = skill >> 1;//or also called 'action'
final int skillIdx = nAction - 30;
~~~
if (skillIdx >= 0 && skillIdx <= 16) {
MobSkillInfo msi = monster.getStats().getMobSkill(skillIdx);
if (msi!= null) {
if (msi.getAfterAttack() != -1) {
monster.getMap().broadcastMessage(MobPacket.setAfterAttack(monster.getObjectId(), msi.getAfterAttack(), nAction, (skill & 1) != 0));
}
if (msi.getSkillAfter() > 0) {
msi.getMobSkill().setMobSkillDelay(c.getPlayer(), monster, msi.getSkillAfter(), option);
return;
}
monster.resetSkillCommand();
msi.getMobSkill().applyEffect(c.getPlayer(), monster, true, option);
}
}
Thank you for reading
Last edited: