Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Fixing Ninja Ambush Skill in MoopleDev

Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
Hi guys, its been a while. Finally got some free time and want to get back into MapleDev. A few years now? I was talking to ERic how the structure of Odin work and it helped a great deal however I still dont understand everything and still will like to learn.

So in MoopleDev the Ninja Ambush skill I believe doesnt work properly as:

1. It does 1 damage or it misses
2. It does do it periodically as its states.
I tracked it down being in the Special Move Handler with the packet:
Code:
5B 00 5A BF 03 00 AC E1 3E 00 1E 01 74 00 00 00 E8 03

After this I dont know what to do.

I tried using a buff that does work in the game to get more familiar with it but it left more questions then answers. For example using MapleWarrior. So here I think packets work in general in regards to Buffs.

Maple Warrior Packet:

Code:
5B 00 7E 02 06 00 A8 E1 3E 00 1E 52 01 4F 01 80 E8 03

1. The client invokes the method messageReceived(IOsession session, Object message)
Where it grabs the packets and parses it knowing which class / handler to invoke to.
Code:
Grabbing Packet: SeekableLittleEndianAccessor slea = newGenericSeekableLittleEndianAccessor(new ByteArrayByteStream(content));

Storing Packet Header: short packetId = slea.readShort();

Identifying which handler: final MaplePacketHandler packetHandler = processor.getHandler(packetId);

Invoking the respective handler: packetHandler.handlePacket(slea, client);

2. It goes into its respective handler class (SpecialMoveHandler):

Code:
Stores your maple Character: MapleCharacter chr = c.getPlayer();

No idea what it does but it reads 4 bytes from the packet?: chr.getAutobanManager().setTimestamp(4, slea.readInt());

Get the skill id from the packet: int skillid = slea.readInt();
Get skill level from Packet: int __skillLevel = slea.readByte();
Creates a Skill object: Skill skill = SkillFactory.getSkill(skillid);

Gets the effect: MapleStatEffect effect = skill.getEffect(skillLevel);

Then Apply the effect ?: skill.getEffect(skillLevel).applyTo(c.getPlayer(), pos);

What I am confused about is that it pops out of the messageRecieved method and then where does it go from there? Doesnt the server need to send the new stats to the client? Which class is that?

Also as you can see I wouldnt know where to start with ninja ambush as its a periodic and does damage buff?

Last thing, when you are reading bytes or shorts from the packet does it keep track for example, if I said slea.readByte() and my packet was lets say 5B 00 30 40 50 30 it would read 5B right? then if I did it again would it read 00 ? and if i did sea.readInt() would it read 0x30 50 40 30?
 
Last edited:
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
If damageMonster() is another visual packet why does it do damage then?
It doesn't: it's the damage() call that follows damageMonster() that actually deals the damage.

I have double check the hp variable and let say blue snail initially ninja ambush would do 14 damage cause of the damage = hp - 1 (15-1) then when I do it again hp would then be 1?
In the same code portion where it checks if damage >= hp (the part where we're adding the extra if , to clarify), there's also a call to run the cancelTask.
You can see the code for this cancelTask , where it's initialized (it's then passed as one of the arguments for the DamageTask).

Basically, if the damage is equal or greater than the monster's HP, it's modified so that it becomes equal to HP - 1, and, at the same time, cancelTask is called so that the whole skill effect can be ended prematurely.
In other words, Ninja Ambush (and other DOTs) will stop being applied as soon as the monster reaches 1 HP.

Why is there two visual ways of showing damage then via damageMonster() and applyMonsterStatus()
Because applyMonsterStatus() only shows the tick damage numbers for the DOT effects like Poison and Ninja Ambush, but is also used for statuses such as Stun and Freeze, which do not have damage numbers.
It's a "special" way of showing damage numbers only for DOTs, and only because that's the way Nexon coded that in their client.

damageMonster(), on the other hand, is used when "independent" damage numbers need to be shown (like when a monster is being hit by the return damage of Power Guard, or when Shadow Web needs to display its dot damage).
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
okay I think I am starting to get it, ill figure out the if statement clause



Yup, I think I did it let me know if this is it

Code:
 else if (type == 2){
                     cancelTask.run();
                     status.getCancelTask().cancel(false);
                }



Or maybe I was too dumb. I' am starting to think nothing is actually wrong with ninja ambush all along? It was my lack of knowledge. The only thing that's wrong I would say is that the damage being passed down with ninja_ambush is an int and not a short.
 
Last edited:
Upvote 0
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
You don't need to add the else : that part of code is also required for the other types, 0 and 1, because all of them need to stop dealing damage once the monster reaches 1 HP.

Only the first if is necessary, because the additional damage display is only needed for Shadow Web. c:
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
You don't need to add the else : that part of code is also required for the other types, 0 and 1, because all of them need to stop dealing damage once the monster reaches 1 HP.

Only the first if is necessary, because the additional damage display is only needed for Shadow Web. c:

Yeah youre right. I am honestly convinced ninja ambush was working all along haha besides the int problem



Okay, so I double check everything and I guess to my current knowledge I think its working fine?. I learned a lot through this process though and thats thanks to you.

I believe the problem getting into this what not knowing how the current code was working and it was just trippy.

1. It was showing miss or (damage = hp -1) damage which I believe it shouldn't if the enemy is 1 hp it should cancel the task. Your code above should do the trick.

2. the damage sent to the applyMonsterStatus() was receiving an int and not a short causing the client I guess to truncate it and or view it as a different type of damage (healing).

Is there anything else I should fix that you know of, I am not going to lie this is really fun !

Once again I have to thank you. Thank you for sticking around and being patient with me and showing me the ropes :)
 
Upvote 0
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
Mmmm, there's likely some other skills that have issues, mainly because of Odin's imprecise way of handling skill casting packets, so your best bet would be to test as many different skills as you can, and make sure they're behaving as intended. Sooner or later, you're gonna find a new test subject for your coding adventures.

And, no worries, I'm glad I could be of help c: I'll be around if you'll need help on other topics, so feel free to ask whenever.
Have fun!
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
Last thing before you go! Where do you find description of skills, like how would I know if a skill is acting up or if its intentional I havent played maple in years kinda forgot a lot of things
 
Upvote 0
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
You can find the descriptions in String.wz, in the "skill.img" node; but that's the same information you'd see when hovering over the skill with your mouse, so that's what you can use as reference.

Also, youtube videos of pre-bigbang Maplestory and websites such as have all sorts of informative materials you could use.

Oh and, some private server forums keep reports about problematic skills publicly available, so you could also browse here and there to figure out which skills tend to give issues most frequently!
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
Thank you and I will for sure come back to you for more questions ahaha



Might have found a potential problem with Combo Tempest (aran skill) it does no damage to boss monsters and doesnt kill enemies well some and that are affected take 8 damage and then die if attack once again with any skill? and where do you find formulas ?



Nvm: maybe combo tempest is suppose to work like that, hahah how do you know if a skill is suppose to show damage or not so confusing
 
Last edited:
Upvote 0
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
Yeah, Tempest is supposed to work like that c: it freezes monsters and causes your next attack to be lethal, regardless of monster's HP.

You could find some formulas on Ayumilove's website; should be relevant, although it may be missing Aran still.
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
Yeah, Tempest is supposed to work like that c: it freezes monsters and causes your next attack to be lethal, regardless of monster's HP.

You could find some formulas on Ayumilove's website; should be relevant, although it may be missing Aran still.
hahah thank you once again, I think I might want to fix it up I think its returning the monsters HP as the lethal attack where as I want your damage to be it instead. EDIT: nvm I think my character glitched or something

oh how is ninja ambush suppose to work when the monster is at HP what damage are you suppose to see, MISS or your max damage or something else?

And do you know anything about upgraded server for example v83 to v90 or v95



and here is my "fix" ninja ambush I want your opinion on it.

Code:
int damage = (int) 2 * (from.getStr() + from.getLuk())  * (skill.getEffect(level).getDamage()/100);   [COLOR=#ff0000] //formula from the site you posted    [/COLOR]                 

 if (damage > 32767) {  [COLOR=#ff0000] //Since the packet cant take anything higher then an int force it out like this      [/COLOR]

  damage = 32767;           

  }

Then:

Code:
if (damge >= hp) {

damage = hp - 1;

if (type == 1) {
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
}

else if (type == 2) { [COLOR=#ff0000]//Essentially if ninja ambush go here[/COLOR]
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), dealDamage), getPosition()); // [COLOR=#ff0000]I didnt want the damge to either come out as nothing, miss, or (hp-1) just seemed of so output maximum damage or the damage calculated[/COLOR]
//[COLOR=#ff0000]it seems like the client knows when the enemy hp is 1 and it wont activate the animation to tick the damage from status.setValue(MonsterStatus.NINJA_AMBUSH, damage);
[/COLOR]}

cancelTask.run();
 status.getCancelTask().cancel(false);
}
 
Last edited:
Upvote 0
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
When the monster reaches 1 HP, Ninja Ambush is supposed to be deactivated prematurely.
That interaction is coded in the cancelTask, and it's what gets activated at the end of the last code portion you quoted. c:

The edits you made look okay!
You should never be able to reach over 32767 dot damage with vanilla v83 skills and stats (Ambush damage at max level is 100, so that formula essentially reduces to 2(str + luk), and those two stats alone can't give 16383 via sum), so you might not need the extra check, unless you're planning to custom edit something.


As for your question about server upgrading, your main concerns would be about updating your current packets (every version changes atleast some of them), and keeping up with the features added with every patch.
Thanks to the v95 source leak, upgrading to that version is a relatively simple task, provided you're prepared to spend time and patience with it.

There's a thread that contains information about "what came when" in GMS, on this forum, although I can't go ahead and link it for you at the moment.
That'll be useful!
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
AHH thank you, ill shall put back the edits that you made. Correct your coded did do that. I just didnt fundmentally knew what ninja ambush was suppose to do. Thank you for that insight.

well if your stats are 32676;) then maybe u can achieve that number haha

whats the main classes I need to do in order to upgrade, whats the process like?

Ill try to find it!
 
Upvote 0
Junior Spellweaver
Joined
Sep 16, 2017
Messages
156
Reaction score
36
whats the main classes I need to do in order to upgrade, whats the process like?
I'm not experienced at all in server version upgrading, so I can only give you "educated guesses" at what you'd need to focus on.

Apart from the obvious (existing opcode value changes, existing packet structures changes, new opcodes), you wanna focus on implementing classes/methods for completely new mechanics.

If you're starting from v83, no matter what your target version is, the first thing that comes to mind is the Evan class, that was partially included in v83, but fully released in v84.
You're gonna need to implement functions to handle the new job values, add the skill IDs to MapleStatEffect's data initialization, and ofcourse add the skill effects.
This release by Eric is gold, when it comes to Evan implementation; keep in mind however that that release refers to implementing Evans in a v83 client, thus you'll need to handle things a bit differently (you won't need client edits, and you'll need to double check the opcode values).

If you're planning to go past v88, Dual Blade class will also require the same kind of work.

Post-Bigbang (v93+), there's a lot of extra work to do, starting from the Resistance classes, to mention the quickest guess.


To have an idea of the features that were added with each new patch, you may refer to in BasilMarket. It won't be a simple task for sure. c:
 
Upvote 0
Newbie Spellweaver
Joined
Aug 27, 2016
Messages
91
Reaction score
1
Exactly why I want to do it, I believe this will finally get me right into mapleDev properly.

I believe I know how to find the headers of a receive and send packet but I dont know the structure you would use them in like in handlers and maplepacketcreator. To send packets to the client and to receive the information of the packet from the client



Ill post an example soon just shoving the v95 into the IDA



Okay Lets look into the CwVsContext__OnPacket: Which is I believe its sendopcode packets. So Its what the server is sending to the client via MaplePacketCreator.java


And lets look at the CWvsContext::OnInventoryOperation. The packet header for this specific case is 0x1C as its what gets triggered to go into this case.

So I would update this in my V.83 source which is currently at 0x1D ->0x1C so it went down.


However this is the problem: if anyone of the mplew. structure changes and I dont know how to find that. Same goes for handlers when you trying to receive a packet.


In MaplePacketCreator.java: Current V83 InvetoryOperation. I believe packet structure also changes

Code:
public static byte[] modifyInventory(boolean updateTick, final List<ModifyInventory> mods) {     
   final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();   
mplew.writeShort(SendOpcode.INVENTORY_OPERATION.getValue());       
   mplew.writeBool(updateTick);  
  mplew.write(mods.size());   
  //mplew.write(0); v104 :)     

  
  int addMovement = -1;       for (ModifyInventory mod : mods) {          
    mplew.write(mod.getMode());        mplew.write(mod.getInventoryType());     
   mplew.writeShort(mod.getMode() == 2 ? mod.getOldPosition() : mod.getPosition());     
       switch (mod.getMode()) {      
          case 0: {//add item               
     addItemInfo(mplew, mod.getItem(), true);         
           break;               
 }     
           case 1: {//update quantity                 
   mplew.writeShort(mod.getQuantity());                 
   break;               
 }   
             case 2: {//move                   
                   mplew.writeShort(mod.getPosition());            
        if (mod.getPosition() < 0 || mod.getOldPosition() < 0) {             
           addMovement = mod.getOldPosition() < 0 ? 1 : 2;          
          }                 
   break;             
   }              
  case 3: {//remove                 
   if (mod.getPosition() < 0) {                 
       addMovement = 2;                
    }                  
  break;               
 }           
 }          
  mod.clear();     
   }      
  if (addMovement > -1) {      
      mplew.write(addMovement);    
    }       
 return mplew.getPacket();    
}
 
Last edited:
Upvote 0
Back
Top