Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 11/06/23 in all areas

  1. Having now mostly finished with custom content for the time being, I decided to take a look at some player complaints that I previously ignored saved for later. Among players who are new to Ragnarok, by far the most common complaints involve the game's netcode and pathfinding. I've played Ragnarok for so long that I've learned to just automatically adjust for it. For example, I know I don't need to tell anyone from here this, but when moving around in RO you generally want to keep the mouse cursor about this far from your character and hold the mouse button down. This works pretty well, as long as you keep the mouse cursor about that close you shouldn't have any issues with your character not going where you want them to. I imagine this is probably kind of a holdover from the very early days where you couldn't zoom the screen out much at all. An RO vet would know you definitely wouldn't ever try to do something like this to move: But it's hard for people who are used to more modern games to get used to this, no matter how many times I pointed out that it was all their own fault. So I decided to see if I could do something with the pathfinding algorithm to make it work a bit better. For starters, it turns out there's actually something incredibly easy you can do to improve pathfinding a lot! All you have to do is disable this line in core.hpp: #define OFFICIAL_WALKPATH And instantly pathfinding is way better! It turns out that rathena's pathfinding algorithm is way better than the one that the official game uses, but they include a reproduction of the original for the sake of accuracy to the originals. Just disable this line and suddenly you can walk about twice as far. The example above works now! However, there are still some situations where it doesn't work, generally when walking a long distance in a straight line, oddly enough. I think this is because the way the pathfinding algorithm works is it tries to explore all possible options to get to a destination, so if there are too many choices (like in a very open area) it kinda gets stuck. You can see this in action in a more compact area like clock tower where the pathfinding is actually really good now. However, we can improve it further. The code that handles pathfinding is in unit.cpp, in unit_walktoxy. We can see that it has two algorithms that it uses, an easy one that attempts straight line movement, and a hard one that is more complex. However, it turns out there are some situations where the easy path finds the answer and the hard one doesn't. As such, what we really want is to try both, but currently it never does this. We want to replace this code: if (!path_search(&wpd, bl->m, bl->x, bl->y, x, y, flag&1, CELL_CHKNOPASS)) // Count walk path cells return 0; With this: if (sd && !(flag & 1)) { // check easy path, but don't give up if it fails if (path_search(&wpd, bl->m, bl->x, bl->y, x, y, 1, CELL_CHKNOPASS)) { ud->state.walk_easy = 1; } else { if (path_search(&wpd, bl->m, bl->x, bl->y, x, y, flag & 1, CELL_CHKNOPASS)) { ud->state.walk_easy = flag & 1; } else return 0; } } else { if (path_search(&wpd, bl->m, bl->x, bl->y, x, y, flag & 1, CELL_CHKNOPASS)) { ud->state.walk_easy = flag & 1; } else return 0; } The idea here is that if the flag is not set to try the easy path, it will do it anyway, but if it fails, it will try the hard path. If the flag is set to try the easy path, it only tries the easy path, as before. Also, it only applies this logic to players and not enemies. You can remove the sd in the first if statement to apply it to enemies as well, but this actually makes the game a little harder as the AI is now better at chasing the player. In any case, pathfinding is now drastically better, you can click at least twice as far away and automatically path around many obstacles. Try it out for yourself, it's very noticeable. Next on the list of complaints was that sometimes when you click on an enemy, you don't attack. This is one that I've encountered a fair bit myself. I'm pretty used to automatically clicking near the enemy to move closer to account for it, but it is pretty annoying and sometimes it can get you killed. It turns out this happens at extremely precise ranges where you're too far to attack but too close to move closer automatically, and then attack. It took me ages to isolate the exact problem, but I finally got it, and the details can be found in this thread: After running a boatload of tests against this I determined it to be fixed, but when partying with a Dancer we were encountering the problem again! How frustrating! But it turns out, this was actually not the same problem at all, but rather a limitation in the client. See, if you read a couple posts above this one, I mentioned that I added a skill to the game that allows Bards and Dancers to enthrall monsters and use them as minions for a little while. The Alchemist also has something similar to this with Bio Cannibalize. In both cases these create mobs that are friendly towards the player because they have their master_id set to the player's id. The server knows that these monsters are not valid targets for the player because the routine battle_check_target checks the master_id, but the client doesn't know this, and so it lets you attempt to target them like any other monster. Note the sword icon on the captivated monster and targeting caret: This is not so much of a problem when out of combat, but when fighting enemies, the minion can kind of get in the way and "absorb" your clicks in a way that another type of minion like a pet or homunculus can't (because they aren't considered targetable by the client). Strictly speaking, this is an unfixable problem because there's no way to tell the client that you shouldn't be able to target that monster, there simply isn't any field that does this within the monster packet. So I opted for a more creative solution. Instead of preventing you from clicking on the monster, the game will simply try to guess what you meant to click on instead, and change your target to that. The way it does this is that if you try to use an enemy-targetting skill or normal attack on a minion (which isn't allowed), it will instead assume that you mean to target whatever the minion is targetting, so it sets your target_id to the minion's target_id. I doubt anyone else would actually need this code (it doesn't really matter for bio cannibalize, it's mostly just for the custom skill on my server, but it's accomplished by this code snippet here in unit_skilluse_id2, which goes right before the comment at the end. if (inf == 1 && battle_check_target(src, target, BCT_ENEMY) <= 0) { if (target->type == BL_MOB && ((TBL_MOB*)target)->master_id && ((TBL_MOB*)target)->target_id && battle_check_target(src, map_id2bl(((TBL_MOB*)target)->target_id), BCT_ENEMY)) { target = map_id2bl(((TBL_MOB*)target)->target_id); target_id = target->id; } } // Fail if the targetted skill is near NPC [Cydh] For some reason that's not totally clear to me, this also requires making this change slightly later when it sets the stepskill: ud->target_to = target->id; It was originally set to target_id instead. If this change is not made it sometimes wrongly sets the target_to to 0. Yet one more complaint was that when you reset your skills, it resets everything, and not just the skills for your current job. I agree that this is annoying, but I looked at it a long time ago and decided it was too hard to fix. See, you would think this would be straightforward, but actually the way the skill trees for the classes are specified, each class knows all the skills it can potentially learn, including those from its previous classes, and there's no real distinction for which skills are new and which ones aren't. So to figure out what skills you can only learn in your current job we have to check the previous jobs and this requires a ton of bitflags and such. Once again, I made a separate thread that has this version of the NPC, if you want this functionality on your server: Finally, I took one more pass on the achievements. I had previously overhauled the achievement system to actually work, but the treasure chest achievements were still a bit lacking. In particular, there were none in many of the newer dungeons (not just the ones I added myself, but many of the official dungeons too), and of course the past area had no treasures at all. So I revamped the treasures, including consolidating some of the categories so I could add a new one for the past areas. This is a lot of work for a feature no one really cares about! Actually, I do kind of think these treasures are useful for discovery - the achievements let you know that certain dungeons exist, so you can keep exploring if you haven't found them all. And then on top of that I also fixed many other maps in the past that had the same map culling issue I previously mentioned for pro_fild01. Geez, that was hard work, I need a vacation. What do you mean I already had my vacation and I spent it all working on Ragnarok?
    1 point
  2. 1 point
  3. Great work. I too like the idea of fully customizable client and this will accomplish it. Too bad that the browsers does not support direct UDP or TCP/IP sockets out of the box. So the wsProxy is used to forward the clients WebSocket to the rAthena server in TCP. WebSockets are little slower in this approach but it feels playable anyway. This is in PWA's perspective and not ChromeApp or what was it. I saw a few possible issues or optimizations when blasting through the sources: Network/PacketRegister.js - 0x84a has duplicate PACKET.CZ.NPC_TRADE_QUIT | PACKET.CZ.CASH_SHOP_CLOSE, later one will override. Skills/SkillDescription.js - has 1309:ish unnecessary join loops, they could just be template strings or concatenation of string values (i'm not talking about Array.concat). Status/StatusInfo.js - ison: "masquerade_laziness.tga" -> typo "icon", not "ison". Renderer/EffectManager.js - First in spamSkillBeforeHit var EF_Init_Par declared object, but is never used. Second the property SkillEffect prop beforeHitEffectIdOnCaster is no where to be found in the entire project, possible naming typo here. Third skill that declare more than one var effects, to use, should declare it at the beginning, and reuse the same variable (same for the var EF_Init_Par)? UI/Components/CashShop - One of the two case 'CartItems': is missing break; UI/Component/PincodeWindow - has empty "t" file I will try to make a pull request if I'm not too busy. Tough these seems minor, since no one noticed. Overall it's nice project and I like it very much. Thanks to all contributing to the project
    1 point
×
×
  • Create New...