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!

Aion 5.8 emulator improvement community project

Joined
Oct 5, 2018
Messages
774
Reaction score
1,144
This solves the problem of the craft not being displayed in the quests and you needing to log in to the game again to display the quest.
Please perform the test and let me know if it works correctly. " "
 

Attachments

You must be registered for see attachments list
Skilled Illusionist
Joined
Nov 8, 2019
Messages
306
Reaction score
301
I am doing the Miragent Holy Templar quest called Loyalty [03940]. The quest says ''Defeat Mission to Kill the Unavarun Legion at the Tempest Islands (0/300) But I can't find the mobs I need to kill anywhere😟

Is it possible for anyone to fix this?😞
 
Newbie Spellweaver
Joined
Feb 25, 2024
Messages
6
Reaction score
3
Hello, I was trying to log in with my external WAN IP but it not works, just with my LAN IP.
The my Aion 5.8 Client and version.dll is from NotAion 5.8. I tested also the Aion 5.8 client from Legendary Aion and I can login on their
Gameserver but not on my local Gameserver.
All needed ports are grantet in my router, also in my WIndows Firewall. Also I have granted remote access to all IP's in my
MySQL. Any ideas to help?
Thank you
 
Newbie Spellweaver
Joined
Oct 23, 2020
Messages
15
Reaction score
27
Hello, I was trying to log in with my external WAN IP but it not works, just with my LAN IP.
The my Aion 5.8 Client and version.dll is from NotAion 5.8. I tested also the Aion 5.8 client from Legendary Aion and I can login on their
Gameserver but not on my local Gameserver.
All needed ports are grantet in my router, also in my WIndows Firewall. Also I have granted remote access to all IP's in my
MySQL. Any ideas to help?
Thank you
Translated via Google
Taken from makeserv.net (by Pr00f & Cacypa 20011) ( )
Also suitable for AL assemblies - 2x, 3x, 4x
{1}
After these changes, other players will be able to access our server via a local network or the Internet.

1. In the file "LoginServer\config\network\network.properties" specify loginserver.network.client.host = *

2. In the file "LoginServer\config\network\network.properties" specify loginserver.network.gameserver.host = *

3. In the file "GameServer\config\default.config" specify gameserver.network.client.host = *

4. In the file "GameServer\config\default.config" specify gameserver.network.login.address = localhost:9014

5. In the database "aion_ls\gameservers" in the mask field we indicate ip 127.0.0.1 (don't forget to also specify id and password)

6. In the ipconfig.xml file we indicate the IP address of the server (I have 212.154.168.243, you have yours):
<ipconfig default="212.154.168.243">
</ipconfig>

7. Delete or comment out lines:
<iprange min="10.0.0.0" max="10.255.255.255" address="10.0.0.0"/>
<iprange min="172.16.0.0" max="172.31.255.255" address="172.16.0.0"/>
<iprange min="192.168.0.0" max="192.168.255.255" address="192.168.0.0"/>


{2}
Is it possible to make a simultaneous connection both via LAN and via the Internet?

Yes, it's possible.
Open the network ranges in the ipconfig.xml file and enter the address of your server on the local network (only in the range in which it is located).
Specify the same address in the launcher for clients sitting in the same local area as the server.

<ipconfig default="External IP of your server">
<!--
IANA-reserved private IPv4 network ranges
Access only from local networks, external client can't have any of
the following ip addresses
-->
<iprange min="10.0.0.0" max="10.255.255.255" address="address of your server on the local network"/>
<iprange min="172.16.0.0" max="172.31.255.255" address="address of your server on the local network"/>
<iprange min="192.168.0.0" max="192.168.255.255" address="address of your server on the local network"/>

</ipconfig>

{3}
If you have already launched the server on your local computer and, after testing, decide to run it over a network or via the Internet using a dedicated IP, then:

1. In the file "LoginServer\config\network\network.properties" - you don't need to change anything!
2. In the file "GameServer\config\default.config" - you don't need to change anything!
3. In the database “your login server database\gameservers” - you don’t need to change anything!
*(asterisk) - means that binding is possible for any available IP address.

4. In the ipconfig.xml file you need to specify the external IP address of your server (the one that is assigned to you by your provider, i.e. your external IP address).
For example:
<ipconfig default="188.111.222.123">


*If you want your server to be accessible both from the Internet and from the local network, do the following:

In the ipconfig.xml file, open the network ranges (i.e. move the arrow (-->) as indicated below) and enter the address of your server on the local network (only in the range in which it is located).
Specify the same address in the launcher for clients sitting in the same local area as the server.

<ipconfig default="External IP of your server">
<!--
IANA-reserved private IPv4 network ranges
Access only from local networks, external client can't have any of
the following ip addresses
-->
<iprange min="10.0.0.0" max="10.255.255.255" address="address of your server on the local network"/>
<iprange min="172.16.0.0" max="172.31.255.255" address="address of your server on the local network"/>
<iprange min="192.168.0.0" max="192.168.255.255" address="address of your server on the local network"/>

</ipconfig>

**with this setting of the ipconfig.xml file, you will be able to enter the game (server) from the same computer on which it is installed, by specifying the address of your server on the local network in the launcher.


Launcher Code:
Echo off
start bin32\aion.bin -ip:188.111.222.123 -port:2106 -cc:7 -lang:en -noweb -nokicks -ncg -noauthgg -ls -charnamemenu

How to create a launcher?: Create a text file with notepad and call it aion.bat, enter the above code into it (where instead of 188.111.222.123 we enter your external IP), save it, distribute it to friends and acquaintances.
 
  • Like
Reactions: jrd
Newbie Spellweaver
Joined
Feb 25, 2024
Messages
6
Reaction score
3
My Gameserver configs are okay, but there is something with remote access, port or database
 
Last edited:
Experienced Elementalist
Joined
Apr 11, 2005
Messages
290
Reaction score
206
I am doing the Miragent Holy Templar quest called Loyalty [03940]. The quest says ''Defeat Mission to Kill the Unavarun Legion at the Tempest Islands (0/300) But I can't find the mobs I need to kill anywhere😟

Is it possible for anyone to fix this?😞
Hello Topaz, here are the mobs you need to kill to quest progress.
 

Attachments

You must be registered for see attachments list
Banned
Banned
Joined
Feb 2, 2011
Messages
24
Reaction score
0
hey guys whats up with the drops, why are the mobs dropping the whole items even if the rates are set to 1x? is it the droptable, does everything have high droprate or is there something else I should check?
 
Joined
Dec 6, 2013
Messages
391
Reaction score
813
hey guys whats up with the drops, why are the mobs dropping the whole items even if the rates are set to 1x? is it the droptable, does everything have high droprate or is there something else I should check?
The drops are quite high in this emulator, you can use less than one for your rate configs, like 0.5. I use 0.5 for XP and 0.6 for drops.
 
Newbie Spellweaver
Joined
Oct 23, 2020
Messages
15
Reaction score
27
It's not because of the drop chance.
The thing is that there are no distribution groups in drop lists, and therefore everything falls at once.
Example
Drop where there is no group
<npc_drop npc_id="210065">
<drop_group name="" use_category="false">
<drop chance="10.0" item_id="101800205" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="101800329" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="101800339" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="102000218" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="102000343" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="102000353" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100000133" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100000390" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100000391" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100100047" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100100272" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100100273" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100200147" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100200390" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600070" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600090" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600299" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600309" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="110100355" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="110300587" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113100552" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113300303" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113300568" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113500328" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113500531" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114100311" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114100560" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114300317" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114300573" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114300583" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114500340" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114500539" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114500549" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="115000325" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="115000550" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="160003551" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="160003557" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000226" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000227" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000228" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000229" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000230" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000231" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000232" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000233" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000235" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="169000003" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="182004393" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="185000106" max_amount="1" min_amount="1"/>
</drop_group>
</npc_drop>
Drop where there are groups
<npc_drop npc_id="210065">
<drop_group name="WEAPON_COMMON_LVL2" use_category="true" race="PC_ALL">
<drop item_id="100000133" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Sword" />
<drop item_id="100000390" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Sword" />
<drop item_id="100000391" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Sword" />
<drop item_id="100100047" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Mace" />
<drop item_id="100100272" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Mace" />
<drop item_id="100100273" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Mace" />
<drop item_id="100200147" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Dagger" />
<drop item_id="100200380" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Dagger" />
<drop item_id="100200390" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Dagger" />
<drop item_id="100600070" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Spellbook" />
<drop item_id="100600299" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Spellbook" />
<drop item_id="100600309" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Spellbook" />
<drop item_id="101800205" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Pistol" />
<drop item_id="101800329" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Pistol" />
<drop item_id="101800339" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Pistol" />
<drop item_id="102000218" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Harp" />
<drop item_id="102000343" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Harp" />
<drop item_id="102000353" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Harp" />
<drop item_id="102100205" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Cipher-Blade" />
<drop item_id="102100312" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Cipher-Blade" />
<drop item_id="102100322" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Cipher-Blade" />
<drop item_id="115000325" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shield" />
<drop item_id="115000550" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shield" />
<drop item_id="115000560" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shield" />
</drop_group>
<drop_group name="ARMOR_COMMON_LVL2" use_category="true" race="PC_ALL">
<drop item_id="110100355" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Tunic" />
<drop item_id="110100616" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Tunic" />
<drop item_id="110100626" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Tunic" />
<drop item_id="110300316" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Jerkin" />
<drop item_id="110300577" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Jerkin" />
<drop item_id="110300587" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Jerkin" />
<drop item_id="110500345" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Hauberk" />
<drop item_id="110500555" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Hauberk" />
<drop item_id="110500565" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Hauberk" />
<drop item_id="113100293" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Leggings" />
<drop item_id="113100542" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Leggings" />
<drop item_id="113100552" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Leggings" />
<drop item_id="113300303" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Breeches" />
<drop item_id="113300558" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Breeches" />
<drop item_id="113300568" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Breeches" />
<drop item_id="113500328" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Chausses" />
<drop item_id="113500531" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Chausses" />
<drop item_id="113500541" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Chausses" />
<drop item_id="114100311" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shoes" />
<drop item_id="114100560" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shoes" />
<drop item_id="114100570" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shoes" />
<drop item_id="114300317" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Boots" />
<drop item_id="114300573" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Boots" />
<drop item_id="114300583" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Boots" />
<drop item_id="114500340" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Brogans" />
<drop item_id="114500539" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Brogans" />
<drop item_id="114500549" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Brogans" />
</drop_group>
<drop_group name="OTHER_COMMON_LVL10" use_category="true" race="PC_ALL">
<drop item_id="160003551" chance="2.90" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Rally Serum" />
<drop item_id="160003557" chance="2.90" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Focus Agent" />
</drop_group>
<drop_group name="MANASTONES_LVL10" use_category="true" race="PC_ALL">
<drop item_id="188054199" chance="0.5" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Manastone Ore" />
</drop_group>
<drop_group name="POWER_SHARD_COMMON" use_category="true" race="PC_ALL">
<drop item_id="169000003" chance="10.00" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Power Shard" />
</drop_group>
<drop_group name="OTHER_JUNK" use_category="true" race="PC_ALL">
<drop item_id="182004393" chance="10.00" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Slink Bone Powder" />
</drop_group>
<drop_group name="QUEST_COMMON" use_category="true" race="PC_ALL">
<drop item_id="182200501" chance="10.00" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Broken Axe Handle" />
</drop_group>
</npc_drop>
 
Joined
Oct 5, 2018
Messages
774
Reaction score
1,144
It's not because of the drop chance.
The thing is that there are no distribution groups in drop lists, and therefore everything falls at once.
Example
Drop where there is no group
<npc_drop npc_id="210065">
<drop_group name="" use_category="false">
<drop chance="10.0" item_id="101800205" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="101800329" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="101800339" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="102000218" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="102000343" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="102000353" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100000133" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100000390" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100000391" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100100047" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100100272" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100100273" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100200147" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100200390" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600070" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600090" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600299" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="100600309" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="110100355" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="110300587" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113100552" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113300303" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113300568" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113500328" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="113500531" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114100311" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114100560" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114300317" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114300573" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114300583" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114500340" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114500539" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="114500549" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="115000325" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="115000550" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="160003551" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="160003557" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000226" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000227" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000228" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000229" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000230" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000231" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000232" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000233" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="167000235" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="169000003" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="182004393" max_amount="1" min_amount="1"/>
<drop chance="10.0" item_id="185000106" max_amount="1" min_amount="1"/>
</drop_group>
</npc_drop>
Drop where there are groups
<npc_drop npc_id="210065">
<drop_group name="WEAPON_COMMON_LVL2" use_category="true" race="PC_ALL">
<drop item_id="100000133" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Sword" />
<drop item_id="100000390" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Sword" />
<drop item_id="100000391" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Sword" />
<drop item_id="100100047" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Mace" />
<drop item_id="100100272" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Mace" />
<drop item_id="100100273" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Mace" />
<drop item_id="100200147" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Dagger" />
<drop item_id="100200380" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Dagger" />
<drop item_id="100200390" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Dagger" />
<drop item_id="100600070" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Spellbook" />
<drop item_id="100600299" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Spellbook" />
<drop item_id="100600309" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Spellbook" />
<drop item_id="101800205" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Pistol" />
<drop item_id="101800329" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Pistol" />
<drop item_id="101800339" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Pistol" />
<drop item_id="102000218" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Harp" />
<drop item_id="102000343" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Harp" />
<drop item_id="102000353" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Harp" />
<drop item_id="102100205" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Cipher-Blade" />
<drop item_id="102100312" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Cipher-Blade" />
<drop item_id="102100322" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Cipher-Blade" />
<drop item_id="115000325" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shield" />
<drop item_id="115000550" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shield" />
<drop item_id="115000560" chance="0.295" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shield" />
</drop_group>
<drop_group name="ARMOR_COMMON_LVL2" use_category="true" race="PC_ALL">
<drop item_id="110100355" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Tunic" />
<drop item_id="110100616" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Tunic" />
<drop item_id="110100626" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Tunic" />
<drop item_id="110300316" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Jerkin" />
<drop item_id="110300577" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Jerkin" />
<drop item_id="110300587" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Jerkin" />
<drop item_id="110500345" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Hauberk" />
<drop item_id="110500555" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Hauberk" />
<drop item_id="110500565" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Hauberk" />
<drop item_id="113100293" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Leggings" />
<drop item_id="113100542" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Leggings" />
<drop item_id="113100552" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Leggings" />
<drop item_id="113300303" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Breeches" />
<drop item_id="113300558" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Breeches" />
<drop item_id="113300568" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Breeches" />
<drop item_id="113500328" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Chausses" />
<drop item_id="113500531" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Chausses" />
<drop item_id="113500541" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Chausses" />
<drop item_id="114100311" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shoes" />
<drop item_id="114100560" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shoes" />
<drop item_id="114100570" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Shoes" />
<drop item_id="114300317" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Boots" />
<drop item_id="114300573" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Boots" />
<drop item_id="114300583" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Boots" />
<drop item_id="114500340" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Brogans" />
<drop item_id="114500539" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Brogans" />
<drop item_id="114500549" chance="0.59" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Plainsman's Brogans" />
</drop_group>
<drop_group name="OTHER_COMMON_LVL10" use_category="true" race="PC_ALL">
<drop item_id="160003551" chance="2.90" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Rally Serum" />
<drop item_id="160003557" chance="2.90" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Focus Agent" />
</drop_group>
<drop_group name="MANASTONES_LVL10" use_category="true" race="PC_ALL">
<drop item_id="188054199" chance="0.5" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Manastone Ore" />
</drop_group>
<drop_group name="POWER_SHARD_COMMON" use_category="true" race="PC_ALL">
<drop item_id="169000003" chance="10.00" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Minor Power Shard" />
</drop_group>
<drop_group name="OTHER_JUNK" use_category="true" race="PC_ALL">
<drop item_id="182004393" chance="10.00" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Slink Bone Powder" />
</drop_group>
<drop_group name="QUEST_COMMON" use_category="true" race="PC_ALL">
<drop item_id="182200501" chance="10.00" min_amount="1" max_amount="1" no_reduce="false" eachmember="false" name="Broken Axe Handle" />
</drop_group>
</npc_drop>
Yes and it's boring to do it one by one, I'm trying but it takes time.
But I'm also only playing on the weekends and there are other things I'm trying to find about drops from this version.
 
Newbie Spellweaver
Joined
Jun 2, 2020
Messages
28
Reaction score
5
Hi guys, thanks for all the efforts you've put into this project. Can anyone share the latest compilation to play? The one released in github was way back Nov 2023.
 
Newbie Spellweaver
Joined
Mar 7, 2022
Messages
56
Reaction score
100
First of all, thank you very much Angry Catster ,for your help and dedication.
I'll share my friend's way about NPCs losing their hate, target or vision and returning around the barrier as well.
This modification only works for the pathfinding version.
It stops returning NPCs from returning to refresh points in a straight line, through walls, and other stupid ways.

These modifications are NpcMoveControlle, AttackManager and ReturningEventHandler.

Java:
package com.aionemu.gameserver.controllers.movement;

import com.aionemu.commons.utils.Rnd;
import com.aionemu.gameserver.ai2.AI2Logger;
import com.aionemu.gameserver.ai2.AIState;
import com.aionemu.gameserver.ai2.AISubState;
import com.aionemu.gameserver.ai2.NpcAI2;
import com.aionemu.gameserver.ai2.handler.TargetEventHandler;
import com.aionemu.gameserver.ai2.manager.WalkManager;
import com.aionemu.gameserver.configs.main.GeoDataConfig;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.model.actions.NpcActions;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.state.CreatureState;
import com.aionemu.gameserver.model.geometry.Point3D;
import com.aionemu.gameserver.model.stats.calc.Stat2;
import com.aionemu.gameserver.model.templates.walker.RouteStep;
import com.aionemu.gameserver.model.templates.walker.WalkerTemplate;
import com.aionemu.gameserver.model.templates.zone.Point2D;
import com.aionemu.gameserver.network.aion.serverpackets.SM_MOVE;
import com.aionemu.gameserver.spawnengine.WalkerFormator;
import com.aionemu.gameserver.spawnengine.WalkerGroup;
import com.aionemu.gameserver.taskmanager.tasks.MoveTaskManager;
import com.aionemu.gameserver.utils.MathUtil;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.collections.LastUsedCache;
import com.aionemu.gameserver.world.World;
import com.aionemu.gameserver.world.geo.GeoService;
import com.aionemu.gameserver.world.geo.nav.NavService;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NpcMoveController
extends CreatureMoveController<Npc> {
    private static final Logger log = LoggerFactory.getLogger(NpcMoveController.class);
    public static final float MOVE_CHECK_OFFSET = 0.1f;
    private static final float MOVE_OFFSET = 0.05f;
    private Destination destination = Destination.TARGET_OBJECT;
    private float pointX;
    private float pointY;
    private float pointZ;
    private LastUsedCache<Byte, Point3D> lastSteps = null;
    private byte stepSequenceNr = 0;
    private float offset = 0.1f;
    List<RouteStep> currentRoute;
    int currentPoint;
    int walkPause;
    private float cachedTargetZ;
    private boolean cachedPathValid;
    private float[][] cachedPath;

    public NpcMoveController(Npc owner) {
        super(owner);
    }
    private static enum Destination {
        TARGET_OBJECT,
        POINT,
        HOME,
    }

    public void moveToTargetObject() {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToTarget started");
            }
            destination = Destination.TARGET_OBJECT;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }

    public void moveToPoint(float x, float y, float z) {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToPoint started");
            }
            cachedPathValid = false;
            destination = Destination.POINT;
            pointX = x;
            pointY = y;
            pointZ = z;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }


    public void moveToHome() {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToHome started");
            }
            cachedPathValid = false;
            float x = owner.getSpawn().getX(), y = owner.getSpawn().getY(), z = owner.getSpawn().getZ();
            destination = Destination.HOME;
            pointX = x;
            pointY = y;
            pointZ = z;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }

    public void moveToNextPoint() {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToNextPoint started");
            }
            destination = Destination.POINT;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }

    @Override
    public void moveToDestination() {
        if (owner.getAi2().isLogging()) {
            AI2Logger.moveinfo(owner, "moveToDestination destination: " + destination);
        }
        if (NpcActions.isAlreadyDead(owner)) {
            abortMove();
            return;
        }
        if (!owner.canPerformMove() || (owner.getAi2().getSubState() == AISubState.CAST)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "moveToDestination can't perform move");
            }
            if (started.compareAndSet(true, false)) {
                setAndSendStopMove(owner);
            }
            updateLastMove();
            return;
        } else if (started.compareAndSet(false, true)) {
            movementMask = MovementMask.NPC_STARTMOVE;
            PacketSendUtility.broadcastPacket(owner, new SM_MOVE(owner));
        }

        if (!started.get()) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "moveToDestination not started");
            }
        }
        switch (destination) {
            case TARGET_OBJECT:
                Npc npc = (Npc) owner;
                VisibleObject target = owner.getTarget();// todo no target
                if (target == null) { //This check is not needed, but I'll leave it for clarity.
                    return;
                }
                if (!(target instanceof Creature)) { //instanceof returns false if target is null.
                    return;
                }
                if ((MathUtil.getDistance(target.getX(),target.getY(),pointZ, pointX, pointY, pointZ) > MOVE_CHECK_OFFSET)) {
                    Creature creature = (Creature) target;
                    offset = npc.getController().getAttackDistanceToTarget();
                    pointX = target.getX();
                    pointY = target.getY();
                    pointZ = getTargetZ(npc, creature);
                    cachedPathValid = false;
                }
                if (!cachedPathValid || cachedPath == null) {
                    cachedPath = NavService.getInstance().navigateToTarget(npc, (Creature) target);
                    if (cachedPath != null) { //Add a bit of randomness to the last point to prevent entities from stacking directly ontop of eachother.
                        //TODO: Move to NavService and make sure this random point is on the navmesh!
                        if (cachedPath.length!=1) {
                            if (Rnd.nextBoolean()) {
                                cachedPath[cachedPath.length - 1][0] += Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            } else {
                                cachedPath[cachedPath.length - 1][0] -= Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            }
                            if (Rnd.nextBoolean()) {
                                cachedPath[cachedPath.length - 1][1] += Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            } else {
                                cachedPath[cachedPath.length - 1][1] -= Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            }
                        }
                    }
                    cachedPathValid = true;
                }
                if (cachedPath != null && cachedPath.length > 0) {
                    float[] p1 = cachedPath[0];
                    assert p1.length == 3;
                    moveToLocation(p1[0], p1[1], getTargetZ(npc, p1[0], p1[1], p1[2]), offset);
                } else {
                    if (cachedPath != null) cachedPath = null;
                    moveToLocation(pointX, pointY, pointZ, offset);
                }
                break;
            case POINT: {//Return path for NPCs (by qingqing: maybe not the best solution)
                this.offset = 0.1f;
                if (!cachedPathValid && cachedPath == null) {
                    cachedPath = NavService.getInstance().navigateToLocation(owner, pointX, pointY, pointZ);
                    cachedPathValid = true;
                }
                if(cachedPath != null && cachedPath.length > 0) {
                    float[] p1 = cachedPath[0];
                    moveToLocation(p1[0], p1[1], getTargetZ(owner, p1[0], p1[1], p1[2]), offset);
                } else {
                    if(cachedPath != null){
                        cachedPath = null;
                    }
                    moveToLocation(this.pointX, this.pointY, this.pointZ, this.offset);
                }
            }
        }
        this.updateLastMove();
    }

    private float getTargetZ(Npc npc, Creature creature) {
        float targetZ = creature.getZ();
        if (GeoDataConfig.GEO_NPC_MOVE && creature.isInFlyingState() && !npc.isInFlyingState()) {
//            if (npc.getGameStats().checkGeoNeedUpdate()) {
            this.cachedTargetZ = GeoService.getInstance().getZ(creature);
 //           }
            targetZ = this.cachedTargetZ;
        }
        return targetZ;
    }
    private float getTargetZ(Npc npc, float x, float y, float z) {
        float targetZ = z;
        if (GeoDataConfig.GEO_NPC_MOVE && !npc.isFlying()) {
//            if (npc.getGameStats().checkGeoNeedUpdate()) {
            cachedTargetZ = GeoService.getInstance().getZ(npc.getWorldId(), x, y, z, 1.1F, npc.getInstanceId());
            targetZ = cachedTargetZ;
//            }
        }
        return targetZ;
    }

    protected void moveToLocation(float targetX, float targetY, float targetZ, float offset) {
        boolean directionChanged = false;
        float ownerX = ((Npc)this.owner).getX();
        float ownerY = ((Npc)this.owner).getY();
        float ownerZ = ((Npc)this.owner).getZ();
        boolean bl = directionChanged = targetX != this.targetDestX || targetY != this.targetDestY || targetZ != this.targetDestZ;
        if (directionChanged) {
            this.heading = (byte)(Math.toDegrees(Math.atan2(targetY - ownerY, targetX - ownerX)) / 3.0);
        }
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "OLD targetDestX: " + this.targetDestX + " targetDestY: " + this.targetDestY + " targetDestZ " + this.targetDestZ);
        }
        if (targetX == 0.0f && targetY == 0.0f) {
            targetX = ((Npc)this.owner).getSpawn().getX();
            targetY = ((Npc)this.owner).getSpawn().getY();
            targetZ = ((Npc)this.owner).getSpawn().getZ();
        }
        this.targetDestX = targetX;
        this.targetDestY = targetY;
        this.targetDestZ = targetZ;
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "ownerX=" + ownerX + " ownerY=" + ownerY + " ownerZ=" + ownerZ);
            AI2Logger.moveinfo((Creature)this.owner, "targetDestX: " + this.targetDestX + " targetDestY: " + this.targetDestY + " targetDestZ " + this.targetDestZ);
        }
        float currentSpeed = ((Npc)this.owner).getGameStats().getMovementSpeedFloat();
        float futureDistPassed = currentSpeed * (float)(System.currentTimeMillis() - this.lastMoveUpdate) / 1000.0f;
        float dist = (float)MathUtil.getDistance(ownerX, ownerY, ownerZ, targetX, targetY, targetZ);
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "futureDist: " + futureDistPassed + " dist: " + dist);
        }
        if (dist == 0.0f) {
            if (((Npc)this.owner).getAi2().getState() == AIState.RETURNING) {
                if (((Npc)this.owner).getAi2().isLogging()) {
                    AI2Logger.moveinfo((Creature)this.owner, "\u72b6\u6001\u8fd4\u56de\uff1a\u4e2d\u6b62\u79fb\u52a8");
                }
                TargetEventHandler.onTargetReached((NpcAI2)((Npc)this.owner).getAi2());
            }
            return;
        }
        if (futureDistPassed > dist) {
            futureDistPassed = dist;
        }
        if (futureDistPassed == dist) {
            if (cachedPath != null && cachedPath.length > 0) {
                float[][] tempCache = new float[cachedPath.length - 1][];
                if (tempCache.length > 0) {
                    System.arraycopy(cachedPath, 1, tempCache, 0, cachedPath.length - 1);
                    cachedPath = tempCache;
                } else {
                    cachedPath = null;
                    cachedPathValid = false;
                }
            }
        }
        float distFraction = futureDistPassed / dist;
        float newX = (this.targetDestX - ownerX) * distFraction + ownerX;
        float newY = (this.targetDestY - ownerY) * distFraction + ownerY;
        float newZ = (this.targetDestZ - ownerZ) * distFraction + ownerZ;
        if (ownerX == newX && ownerY == newY && ((Npc)this.owner).getSpawn().getRandomWalk() > 0) {
            return;
        }
        if (GeoDataConfig.GEO_NPC_MOVE && GeoDataConfig.GEO_ENABLE && owner.getAi2().getSubState() != AISubState.WALK_PATH && owner.getAi2().getState() != AIState.RETURNING
                && owner.getGameStats().checkGeoNeedUpdate()) {
            if (owner.getSpawn().getX() != targetDestX || owner.getSpawn().getY() != targetDestY || owner.getSpawn().getZ() != targetDestZ) {
                float geoZ = GeoService.getInstance().getZ(owner.getWorldId(), newX, newY, newZ, 0, owner.getInstanceId());
                if (Math.abs(newZ - geoZ) > 1) {
                    directionChanged = true;
                }
                newZ = geoZ ;
            }
        }
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "newX=" + newX + " newY=" + newY + " newZ=" + newZ + " mask=" + this.movementMask);
        }
        World.getInstance().updatePosition(this.owner, newX, newY, newZ, this.heading, false);
        byte newMask = this.getMoveMask(directionChanged);
        if (this.movementMask != newMask) {
            if (((Npc)this.owner).getAi2().isLogging()) {
                AI2Logger.moveinfo((Creature)this.owner, "oldMask=" + this.movementMask + " newMask=" + newMask);
            }
            this.movementMask = newMask;
            PacketSendUtility.broadcastPacket(this.owner, new SM_MOVE((Creature)this.owner));
        }
    }

    private byte getMoveMask(boolean directionChanged) {
        if (directionChanged) {
            return -32;
        }
        if (((Npc)this.owner).getAi2().getState() == AIState.RETURNING) {
            return -30;
        }
        if (((Npc)this.owner).getAi2().getState() == AIState.FOLLOWING) {
            return -22;
        }
        byte mask = 0;
        Stat2 stat = ((Npc)this.owner).getGameStats().getMovementSpeed();
        if (((Npc)this.owner).isInState(CreatureState.WEAPON_EQUIPPED)) {
            mask = stat.getBonus() < 0 ? (byte)-30 : -28;
        } else if (((Npc)this.owner).isInState(CreatureState.WALKING) || ((Npc)this.owner).isInState(CreatureState.ACTIVE)) {
            byte by = mask = stat.getBonus() < 0 ? (byte)-24 : -22;
        }
        if (((Npc)this.owner).isFlying()) {
            mask = (byte)(mask | 4);
        }
        return mask;
    }

    @Override
    public void abortMove() {
        if (!this.started.get()) {
            return;
        }
        this.resetMove();
        this.setAndSendStopMove((Creature)this.owner);
    }

    public void resetMove() {
        if (owner.getAi2().isLogging()) {
            AI2Logger.moveinfo(owner, "MC perform stop");
        }
        started.set(false);
        targetDestX = 0;
        targetDestY = 0;
        targetDestZ = 0;
        pointX = 0;
        pointY = 0;
        pointZ = 0;
    }

    public void setCurrentRoute(List<RouteStep> currentRoute) {
        if (currentRoute == null) {
            AI2Logger.info(owner.getAi2(), String.format("MC: setCurrentRoute is setting route to null (NPC id: {})!!!", owner.getNpcId()));
        } else {
            this.currentRoute = currentRoute;
        }
        this.currentPoint = 0;
    }

    public void setRouteStep(RouteStep paramRouteStep1, RouteStep paramRouteStep2) {
        Point2D localPoint2D = null;
        if (((Npc)this.owner).getWalkerGroup() != null) {
            if (((Npc)this.owner).getWalkerGroupShift() == null) {
                log.warn("Missing WalkerGroupShift for: " + ((Npc)this.owner).getNpcId());
                return;
            }
            localPoint2D = WalkerGroup.getLinePoint(new Point2D(paramRouteStep2.getX(), paramRouteStep2.getY()), new Point2D(paramRouteStep1.getX(), paramRouteStep1.getY()), ((Npc)this.owner).getWalkerGroupShift());
            this.pointZ = paramRouteStep2.getZ();
            if (GeoDataConfig.GEO_ENABLE && GeoDataConfig.GEO_NPC_MOVE && !(this.owner.isInFlyingState())) {
                this.pointZ = GeoService.getInstance().getZ(((Creature)this.owner).getWorldId(), paramRouteStep2.getX(), paramRouteStep2.getY(), paramRouteStep2.getZ()-1, 100f, 1);
            }
            ((Npc)this.owner).getWalkerGroup().setStep((Npc)this.owner, paramRouteStep1.getRouteStep());
        } else {
            this.pointZ = paramRouteStep1.getZ();
            if (GeoDataConfig.GEO_ENABLE && GeoDataConfig.GEO_NPC_MOVE && !(this.owner.isInFlyingState())) {
                this.pointZ = GeoService.getInstance().getZ(((Creature)this.owner).getWorldId(), paramRouteStep1.getX(), paramRouteStep1.getY(), paramRouteStep1.getZ()-1, 100f, 1);
            }
        }
        this.currentPoint = paramRouteStep1.getRouteStep() - 1;
        this.pointX = localPoint2D == null ? paramRouteStep1.getX() : localPoint2D.getX();
        this.pointY = localPoint2D == null ? paramRouteStep1.getY() : localPoint2D.getY();
        this.destination = Destination.POINT;
        this.walkPause = paramRouteStep1.getRestTime();
    }

    public int getCurrentPoint() {
        return this.currentPoint;
    }

    public boolean isReachedPoint() {
        return MathUtil.getDistance(((Npc)this.owner).getX(), ((Npc)this.owner).getY(), ((Npc)this.owner).getZ(), this.pointX, this.pointY, this.pointZ) < (double)0.05f;
    }

    public void chooseNextStep() {
        int oldPoint = this.currentPoint;
        if (this.currentRoute == null) {
            WalkerTemplate template;
            WalkManager.stopWalking((NpcAI2)((Npc)this.owner).getAi2());
            if (!WalkerFormator.processClusteredNpc((Npc)this.owner, ((Npc)this.owner).getWorldId(), ((Npc)this.owner).getInstanceId()) && (template = DataManager.WALKER_DATA.getWalkerTemplate(((Npc)this.owner).getSpawn().getWalkerId())) != null) {
                this.currentRoute = template.getRouteSteps();
            }
            if (this.currentRoute == null) {
                log.warn("Bad Walker Id: " + ((Npc)this.owner).getNpcId() + " - point: " + oldPoint);
                return;
            }
        }
        this.currentPoint = this.currentPoint < this.currentRoute.size() - 1 ? ++this.currentPoint : 0;
        this.setRouteStep(this.currentRoute.get(this.currentPoint), this.currentRoute.get(oldPoint));
    }

    public int getWalkPause() {
        return this.walkPause;
    }

    public boolean isChangingDirection() {
        return this.currentPoint == 0;
    }

    @Override
    public final float getTargetX2() {
        return this.started.get() ? this.targetDestX : ((Npc)this.owner).getX();
    }

    @Override
    public final float getTargetY2() {
        return this.started.get() ? this.targetDestY : ((Npc)this.owner).getY();
    }

    @Override
    public final float getTargetZ2() {
        return this.started.get() ? this.targetDestZ : ((Npc)this.owner).getZ();
    }

    public boolean isFollowingTarget() {
        return this.destination == Destination.TARGET_OBJECT;
    }

    public void storeStep() {
        if (((Npc)this.owner).getAi2().getState() == AIState.RETURNING) {
            return;
        }
        if (this.lastSteps == null) {
            this.lastSteps = new LastUsedCache(10);
        }
        Point3D currentStep = new Point3D(((Npc)this.owner).getX(), ((Npc)this.owner).getY(), ((Npc)this.owner).getZ());
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "store back step: X=" + ((Npc)this.owner).getX() + " Y=" + ((Npc)this.owner).getY() + " Z=" + ((Npc)this.owner).getZ());
        }
        if (this.stepSequenceNr == 0 || MathUtil.getDistance(this.lastSteps.get(this.stepSequenceNr), currentStep) >= 5.0) {
            this.stepSequenceNr = (byte)(this.stepSequenceNr + 1);
            this.lastSteps.put(this.stepSequenceNr, currentStep);
        }
    }

    public Point3D recallPreviousStep() {
        Point3D result =  stepSequenceNr == 0 ? null : lastSteps.get(stepSequenceNr--);
        Point3D point3D;
        if (this.lastSteps == null) {
            this.lastSteps = new LastUsedCache(10);
        }
        if (this.stepSequenceNr == 0) {
            point3D = null;
        } else {
            byte by = this.stepSequenceNr;
            this.stepSequenceNr = (byte)(by - 1);
            point3D = result = this.lastSteps.get(by);
        }
        if (result == null) {
            if (((Npc)this.owner).getAi2().isLogging()) {
                AI2Logger.moveinfo((Creature)this.owner, "recall back step: spawn point");
            }
            this.targetDestX = ((Npc)this.owner).getSpawn().getX();
            this.targetDestY = ((Npc)this.owner).getSpawn().getY();
            this.targetDestZ = ((Npc)this.owner).getSpawn().getZ();
            result = new Point3D(this.targetDestX, this.targetDestY, this.targetDestZ);
        } else {
            if (((Npc)this.owner).getAi2().isLogging()) {
                AI2Logger.moveinfo((Creature)this.owner, "recall back step: X=" + result.getX() + " Y=" + result.getY() + " Z=" + result.getZ());
            }
            this.targetDestX = result.getX();
            this.targetDestY = result.getY();
            this.targetDestZ = result.getZ();
        }
        return result;
    }

    public void clearBackSteps() {
        this.stepSequenceNr = 0;
        this.lastSteps = null;
        this.movementMask = 0;
    }

    @Override
    public void skillMovement() {
        // TODO Auto-generated method stub
    }
}

Java:
package com.aionemu.gameserver.ai2.manager;

import com.aionemu.gameserver.ai2.AI2Logger;
import com.aionemu.gameserver.ai2.AISubState;
import com.aionemu.gameserver.ai2.AttackIntention;
import com.aionemu.gameserver.ai2.NpcAI2;
import com.aionemu.gameserver.ai2.event.AIEventType;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.configs.main.GeoDataConfig;
import com.aionemu.gameserver.world.geo.GeoService;
import com.aionemu.gameserver.world.geo.nav.NavData;
import com.aionemu.gameserver.world.geo.nav.NavService;

/**
 * @author ATracer
 * @modified Yon (Aion Reconstruction Project) -- removed non-retail-like leash in {@link #checkGiveupDistance(NpcAI2)}.
 */
public class AttackManager {

    /**
     * @param npcAI
     */
    public static void startAttacking(NpcAI2 npcAI) {
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: startAttacking");
        }
        npcAI.getOwner().getGameStats().setFightStartingTime();
        EmoteManager.emoteStartAttacking(npcAI.getOwner());
        scheduleNextAttack(npcAI);
    }

    /**
     * @param npcAI
     */
    public static void scheduleNextAttack(NpcAI2 npcAI) {
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: scheduleNextAttack");
        }
        // don't start attack while in casting substate
        AISubState subState = npcAI.getSubState();
        if (subState == AISubState.NONE) {
            chooseAttack(npcAI, npcAI.getOwner().getGameStats().getNextAttackInterval());
        } else {
            if (npcAI.isLogging()) {
                AI2Logger.info(npcAI, "Will not choose attack in substate" + subState);
            }
        }
    }

    /**
     * choose attack type
     */
    protected static void chooseAttack(NpcAI2 npcAI, int delay) {
        AttackIntention attackIntention = npcAI.chooseAttackIntention();
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: chooseAttack " + attackIntention + " delay " + delay);
        }
        if (!npcAI.canThink()) {
            return;
        }
        switch (attackIntention) {
        case SIMPLE_ATTACK:
            SimpleAttackManager.performAttack(npcAI, delay);
            break;
        case SKILL_ATTACK:
            SkillAttackManager.performAttack(npcAI, delay);
            break;
        case FINISH_ATTACK:
            npcAI.think();
            break;
        default:
            break;
        }
    }

    /**
     * @param npcAI
     */
    public static void targetTooFar(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: attackTimeDelta " + npc.getGameStats().getLastAttackTimeDelta());
        }

        // switch target if there is more hated creature
        if (npc.getGameStats().getLastChangeTargetTimeDelta() > 5) {
            Creature mostHated = npc.getAggroList().getMostHated();
            if (mostHated != null && !mostHated.getLifeStats().isAlreadyDead()
                    && !npc.isTargeting(mostHated.getObjectId())) {
                if (npcAI.isLogging()) {
                    AI2Logger.info(npcAI, "AttackManager: switching target during chase");
                }
                npcAI.onCreatureEvent(AIEventType.TARGET_CHANGED, mostHated);
                return;
            }
        }
        if (!npc.canSee((Creature) npc.getTarget())) {
            npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
            return;
        }
        if (checkGiveupDistance(npcAI)) {
            npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
            return;
        }
        //Check the NAV navigation on status.
        if (!GeoDataConfig.GEO_NAV_ENABLE) {
            //If the NPC loses its target or vision.
            if (!GeoService.getInstance().canSee(npc,npc.getTarget())) {
            npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
            return;
            }
        
        }
        if (npcAI.isMoveSupported()){
            npc.getMoveController().moveToTargetObject();
            return;
        }
        npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
    }

    private static boolean checkGiveupDistance(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        // if target run away too far
        float distanceToTarget = npc.getDistanceToTarget();
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: distanceToTarget " + distanceToTarget);
        }
        // TODO may be ask AI too
        int chaseTarget = npc.isBoss() ? 200 : npc.getPosition().getWorldMapInstance().getTemplate().getAiInfo().getChaseTarget();
        if (distanceToTarget > chaseTarget) {
            return true;
        }
        
        double distanceToHome = npc.getDistanceToSpawnLocation();
        int chaseHome = npc.isBoss() ? 350 : npc.getPosition().getWorldMapInstance().getTemplate().getAiInfo().getChaseHome();
//        if (distanceToHome > chaseHome) { //Leashes like this don't exist on retail (with very few exceptions)
//            return true;
//        }
        // if npc is far away from home
        // start thinking about home after 100 meters and no attack for 10 seconds (only for default monsters)
        if (chaseHome <= 200 || distanceToHome > chaseHome) { // TODO: Check Client and use chase_user_by_trace value
            if ((npc.getGameStats().getLastAttackTimeDelta() > 10 && npc.getGameStats().getLastAttackedTimeDelta() > 10)
                    /*|| (distanceToHome > chaseHome / 2 && npc.getGameStats().getLastAttackedTimeDelta() > 10)*/) {
                return true;
            }
        }
        return false;
    }
}

Java:
package com.aionemu.gameserver.ai2.handler;

import com.aionemu.gameserver.ai2.AI2Logger;
import com.aionemu.gameserver.ai2.AIState;
import com.aionemu.gameserver.ai2.NpcAI2;
import com.aionemu.gameserver.ai2.manager.EmoteManager;
import com.aionemu.gameserver.ai2.manager.WalkManager;
import com.aionemu.gameserver.model.geometry.Point3D;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.templates.spawns.SpawnTemplate;
import com.aionemu.gameserver.spawnengine.SpawnEngine;
import com.aionemu.gameserver.world.World;
import com.aionemu.gameserver.world.geo.GeoService;
import com.aionemu.gameserver.world.geo.nav.NavService;

import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author ATracer
 */
public class ReturningEventHandler {
    private static final Logger log = LoggerFactory.getLogger(ReturningEventHandler.class);
    
    /**
     * @param npcAI
     */
    public static void onNotAtHome(NpcAI2 npcAI) {
        
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "onNotAtHome");
        }
        if (npcAI.setStateIfNot(AIState.RETURNING)) {
            if (npcAI.isLogging()) {
                AI2Logger.info(npcAI, "returning and restoring");
            }
            EmoteManager.emoteStartReturning(npcAI.getOwner());
        }//Rewriting the Return to Pathfinding Mode method
        if (npcAI.isInState(AIState.RETURNING)) {
            Npc npc = npcAI.getOwner();
            Point3D distination = new Point3D(npc.getSpawn().getX(),npc.getSpawn().getY(),npc.getSpawn().getZ());
            if(distination != null){
               npcAI.getOwner().getMoveController().moveToPoint(distination.getX(),distination.getY(),distination.getZ());
            }
        }
    }
    /**
     * @param npcAI
     */
    //Reordering the onBackHome method
    public static void onBackHome(NpcAI2 npcAI) {
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "onBackHome");
        }
        Npc npc = npcAI.getOwner();
        npc.getMoveController().clearBackSteps();
        if (npcAI.setStateIfNot(AIState.IDLE)) {
            EmoteManager.emoteStartIdling(npcAI.getOwner());
            ThinkEventHandler.thinkIdle(npcAI);
        }
        npc.getController().onReturnHome();
    }
}

GNU Lesser Public License and other information that I didn't include, please fill them in when editing to github once the test works. I really don't know how they are written to fit the format of the project.
If the above code is valid, please give the contribution to my friend qingqing.

The updates and modifications to this project are currently trending quiet, thanks to the many friends who have given and contributed.

My English is not good, so I hope it won't affect your reading. :giggle:
 
Newbie Spellweaver
Joined
Aug 11, 2022
Messages
58
Reaction score
178
First of all, thank you very much Angry Catster ,for your help and dedication.
I'll share my friend's way about NPCs losing their hate, target or vision and returning around the barrier as well.
This modification only works for the pathfinding version.
It stops returning NPCs from returning to refresh points in a straight line, through walls, and other stupid ways.

These modifications are NpcMoveControlle, AttackManager and ReturningEventHandler.

Java:
package com.aionemu.gameserver.controllers.movement;

import com.aionemu.commons.utils.Rnd;
import com.aionemu.gameserver.ai2.AI2Logger;
import com.aionemu.gameserver.ai2.AIState;
import com.aionemu.gameserver.ai2.AISubState;
import com.aionemu.gameserver.ai2.NpcAI2;
import com.aionemu.gameserver.ai2.handler.TargetEventHandler;
import com.aionemu.gameserver.ai2.manager.WalkManager;
import com.aionemu.gameserver.configs.main.GeoDataConfig;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.model.actions.NpcActions;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.state.CreatureState;
import com.aionemu.gameserver.model.geometry.Point3D;
import com.aionemu.gameserver.model.stats.calc.Stat2;
import com.aionemu.gameserver.model.templates.walker.RouteStep;
import com.aionemu.gameserver.model.templates.walker.WalkerTemplate;
import com.aionemu.gameserver.model.templates.zone.Point2D;
import com.aionemu.gameserver.network.aion.serverpackets.SM_MOVE;
import com.aionemu.gameserver.spawnengine.WalkerFormator;
import com.aionemu.gameserver.spawnengine.WalkerGroup;
import com.aionemu.gameserver.taskmanager.tasks.MoveTaskManager;
import com.aionemu.gameserver.utils.MathUtil;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.collections.LastUsedCache;
import com.aionemu.gameserver.world.World;
import com.aionemu.gameserver.world.geo.GeoService;
import com.aionemu.gameserver.world.geo.nav.NavService;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NpcMoveController
extends CreatureMoveController<Npc> {
    private static final Logger log = LoggerFactory.getLogger(NpcMoveController.class);
    public static final float MOVE_CHECK_OFFSET = 0.1f;
    private static final float MOVE_OFFSET = 0.05f;
    private Destination destination = Destination.TARGET_OBJECT;
    private float pointX;
    private float pointY;
    private float pointZ;
    private LastUsedCache<Byte, Point3D> lastSteps = null;
    private byte stepSequenceNr = 0;
    private float offset = 0.1f;
    List<RouteStep> currentRoute;
    int currentPoint;
    int walkPause;
    private float cachedTargetZ;
    private boolean cachedPathValid;
    private float[][] cachedPath;

    public NpcMoveController(Npc owner) {
        super(owner);
    }
    private static enum Destination {
        TARGET_OBJECT,
        POINT,
        HOME,
    }

    public void moveToTargetObject() {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToTarget started");
            }
            destination = Destination.TARGET_OBJECT;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }

    public void moveToPoint(float x, float y, float z) {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToPoint started");
            }
            cachedPathValid = false;
            destination = Destination.POINT;
            pointX = x;
            pointY = y;
            pointZ = z;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }


    public void moveToHome() {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToHome started");
            }
            cachedPathValid = false;
            float x = owner.getSpawn().getX(), y = owner.getSpawn().getY(), z = owner.getSpawn().getZ();
            destination = Destination.HOME;
            pointX = x;
            pointY = y;
            pointZ = z;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }

    public void moveToNextPoint() {
        if (started.compareAndSet(false, true)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "MC: moveToNextPoint started");
            }
            destination = Destination.POINT;
            updateLastMove();
            MoveTaskManager.getInstance().addCreature(owner);
        }
    }

    @Override
    public void moveToDestination() {
        if (owner.getAi2().isLogging()) {
            AI2Logger.moveinfo(owner, "moveToDestination destination: " + destination);
        }
        if (NpcActions.isAlreadyDead(owner)) {
            abortMove();
            return;
        }
        if (!owner.canPerformMove() || (owner.getAi2().getSubState() == AISubState.CAST)) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "moveToDestination can't perform move");
            }
            if (started.compareAndSet(true, false)) {
                setAndSendStopMove(owner);
            }
            updateLastMove();
            return;
        } else if (started.compareAndSet(false, true)) {
            movementMask = MovementMask.NPC_STARTMOVE;
            PacketSendUtility.broadcastPacket(owner, new SM_MOVE(owner));
        }

        if (!started.get()) {
            if (owner.getAi2().isLogging()) {
                AI2Logger.moveinfo(owner, "moveToDestination not started");
            }
        }
        switch (destination) {
            case TARGET_OBJECT:
                Npc npc = (Npc) owner;
                VisibleObject target = owner.getTarget();// todo no target
                if (target == null) { //This check is not needed, but I'll leave it for clarity.
                    return;
                }
                if (!(target instanceof Creature)) { //instanceof returns false if target is null.
                    return;
                }
                if ((MathUtil.getDistance(target.getX(),target.getY(),pointZ, pointX, pointY, pointZ) > MOVE_CHECK_OFFSET)) {
                    Creature creature = (Creature) target;
                    offset = npc.getController().getAttackDistanceToTarget();
                    pointX = target.getX();
                    pointY = target.getY();
                    pointZ = getTargetZ(npc, creature);
                    cachedPathValid = false;
                }
                if (!cachedPathValid || cachedPath == null) {
                    cachedPath = NavService.getInstance().navigateToTarget(npc, (Creature) target);
                    if (cachedPath != null) { //Add a bit of randomness to the last point to prevent entities from stacking directly ontop of eachother.
                        //TODO: Move to NavService and make sure this random point is on the navmesh!
                        if (cachedPath.length!=1) {
                            if (Rnd.nextBoolean()) {
                                cachedPath[cachedPath.length - 1][0] += Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            } else {
                                cachedPath[cachedPath.length - 1][0] -= Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            }
                            if (Rnd.nextBoolean()) {
                                cachedPath[cachedPath.length - 1][1] += Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            } else {
                                cachedPath[cachedPath.length - 1][1] -= Rnd.nextDouble() * owner.getObjectTemplate().getBoundRadius().getSide();
                            }
                        }
                    }
                    cachedPathValid = true;
                }
                if (cachedPath != null && cachedPath.length > 0) {
                    float[] p1 = cachedPath[0];
                    assert p1.length == 3;
                    moveToLocation(p1[0], p1[1], getTargetZ(npc, p1[0], p1[1], p1[2]), offset);
                } else {
                    if (cachedPath != null) cachedPath = null;
                    moveToLocation(pointX, pointY, pointZ, offset);
                }
                break;
            case POINT: {//Return path for NPCs (by qingqing: maybe not the best solution)
                this.offset = 0.1f;
                if (!cachedPathValid && cachedPath == null) {
                    cachedPath = NavService.getInstance().navigateToLocation(owner, pointX, pointY, pointZ);
                    cachedPathValid = true;
                }
                if(cachedPath != null && cachedPath.length > 0) {
                    float[] p1 = cachedPath[0];
                    moveToLocation(p1[0], p1[1], getTargetZ(owner, p1[0], p1[1], p1[2]), offset);
                } else {
                    if(cachedPath != null){
                        cachedPath = null;
                    }
                    moveToLocation(this.pointX, this.pointY, this.pointZ, this.offset);
                }
            }
        }
        this.updateLastMove();
    }

    private float getTargetZ(Npc npc, Creature creature) {
        float targetZ = creature.getZ();
        if (GeoDataConfig.GEO_NPC_MOVE && creature.isInFlyingState() && !npc.isInFlyingState()) {
//            if (npc.getGameStats().checkGeoNeedUpdate()) {
            this.cachedTargetZ = GeoService.getInstance().getZ(creature);
 //           }
            targetZ = this.cachedTargetZ;
        }
        return targetZ;
    }
    private float getTargetZ(Npc npc, float x, float y, float z) {
        float targetZ = z;
        if (GeoDataConfig.GEO_NPC_MOVE && !npc.isFlying()) {
//            if (npc.getGameStats().checkGeoNeedUpdate()) {
            cachedTargetZ = GeoService.getInstance().getZ(npc.getWorldId(), x, y, z, 1.1F, npc.getInstanceId());
            targetZ = cachedTargetZ;
//            }
        }
        return targetZ;
    }

    protected void moveToLocation(float targetX, float targetY, float targetZ, float offset) {
        boolean directionChanged = false;
        float ownerX = ((Npc)this.owner).getX();
        float ownerY = ((Npc)this.owner).getY();
        float ownerZ = ((Npc)this.owner).getZ();
        boolean bl = directionChanged = targetX != this.targetDestX || targetY != this.targetDestY || targetZ != this.targetDestZ;
        if (directionChanged) {
            this.heading = (byte)(Math.toDegrees(Math.atan2(targetY - ownerY, targetX - ownerX)) / 3.0);
        }
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "OLD targetDestX: " + this.targetDestX + " targetDestY: " + this.targetDestY + " targetDestZ " + this.targetDestZ);
        }
        if (targetX == 0.0f && targetY == 0.0f) {
            targetX = ((Npc)this.owner).getSpawn().getX();
            targetY = ((Npc)this.owner).getSpawn().getY();
            targetZ = ((Npc)this.owner).getSpawn().getZ();
        }
        this.targetDestX = targetX;
        this.targetDestY = targetY;
        this.targetDestZ = targetZ;
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "ownerX=" + ownerX + " ownerY=" + ownerY + " ownerZ=" + ownerZ);
            AI2Logger.moveinfo((Creature)this.owner, "targetDestX: " + this.targetDestX + " targetDestY: " + this.targetDestY + " targetDestZ " + this.targetDestZ);
        }
        float currentSpeed = ((Npc)this.owner).getGameStats().getMovementSpeedFloat();
        float futureDistPassed = currentSpeed * (float)(System.currentTimeMillis() - this.lastMoveUpdate) / 1000.0f;
        float dist = (float)MathUtil.getDistance(ownerX, ownerY, ownerZ, targetX, targetY, targetZ);
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "futureDist: " + futureDistPassed + " dist: " + dist);
        }
        if (dist == 0.0f) {
            if (((Npc)this.owner).getAi2().getState() == AIState.RETURNING) {
                if (((Npc)this.owner).getAi2().isLogging()) {
                    AI2Logger.moveinfo((Creature)this.owner, "\u72b6\u6001\u8fd4\u56de\uff1a\u4e2d\u6b62\u79fb\u52a8");
                }
                TargetEventHandler.onTargetReached((NpcAI2)((Npc)this.owner).getAi2());
            }
            return;
        }
        if (futureDistPassed > dist) {
            futureDistPassed = dist;
        }
        if (futureDistPassed == dist) {
            if (cachedPath != null && cachedPath.length > 0) {
                float[][] tempCache = new float[cachedPath.length - 1][];
                if (tempCache.length > 0) {
                    System.arraycopy(cachedPath, 1, tempCache, 0, cachedPath.length - 1);
                    cachedPath = tempCache;
                } else {
                    cachedPath = null;
                    cachedPathValid = false;
                }
            }
        }
        float distFraction = futureDistPassed / dist;
        float newX = (this.targetDestX - ownerX) * distFraction + ownerX;
        float newY = (this.targetDestY - ownerY) * distFraction + ownerY;
        float newZ = (this.targetDestZ - ownerZ) * distFraction + ownerZ;
        if (ownerX == newX && ownerY == newY && ((Npc)this.owner).getSpawn().getRandomWalk() > 0) {
            return;
        }
        if (GeoDataConfig.GEO_NPC_MOVE && GeoDataConfig.GEO_ENABLE && owner.getAi2().getSubState() != AISubState.WALK_PATH && owner.getAi2().getState() != AIState.RETURNING
                && owner.getGameStats().checkGeoNeedUpdate()) {
            if (owner.getSpawn().getX() != targetDestX || owner.getSpawn().getY() != targetDestY || owner.getSpawn().getZ() != targetDestZ) {
                float geoZ = GeoService.getInstance().getZ(owner.getWorldId(), newX, newY, newZ, 0, owner.getInstanceId());
                if (Math.abs(newZ - geoZ) > 1) {
                    directionChanged = true;
                }
                newZ = geoZ ;
            }
        }
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "newX=" + newX + " newY=" + newY + " newZ=" + newZ + " mask=" + this.movementMask);
        }
        World.getInstance().updatePosition(this.owner, newX, newY, newZ, this.heading, false);
        byte newMask = this.getMoveMask(directionChanged);
        if (this.movementMask != newMask) {
            if (((Npc)this.owner).getAi2().isLogging()) {
                AI2Logger.moveinfo((Creature)this.owner, "oldMask=" + this.movementMask + " newMask=" + newMask);
            }
            this.movementMask = newMask;
            PacketSendUtility.broadcastPacket(this.owner, new SM_MOVE((Creature)this.owner));
        }
    }

    private byte getMoveMask(boolean directionChanged) {
        if (directionChanged) {
            return -32;
        }
        if (((Npc)this.owner).getAi2().getState() == AIState.RETURNING) {
            return -30;
        }
        if (((Npc)this.owner).getAi2().getState() == AIState.FOLLOWING) {
            return -22;
        }
        byte mask = 0;
        Stat2 stat = ((Npc)this.owner).getGameStats().getMovementSpeed();
        if (((Npc)this.owner).isInState(CreatureState.WEAPON_EQUIPPED)) {
            mask = stat.getBonus() < 0 ? (byte)-30 : -28;
        } else if (((Npc)this.owner).isInState(CreatureState.WALKING) || ((Npc)this.owner).isInState(CreatureState.ACTIVE)) {
            byte by = mask = stat.getBonus() < 0 ? (byte)-24 : -22;
        }
        if (((Npc)this.owner).isFlying()) {
            mask = (byte)(mask | 4);
        }
        return mask;
    }

    @Override
    public void abortMove() {
        if (!this.started.get()) {
            return;
        }
        this.resetMove();
        this.setAndSendStopMove((Creature)this.owner);
    }

    public void resetMove() {
        if (owner.getAi2().isLogging()) {
            AI2Logger.moveinfo(owner, "MC perform stop");
        }
        started.set(false);
        targetDestX = 0;
        targetDestY = 0;
        targetDestZ = 0;
        pointX = 0;
        pointY = 0;
        pointZ = 0;
    }

    public void setCurrentRoute(List<RouteStep> currentRoute) {
        if (currentRoute == null) {
            AI2Logger.info(owner.getAi2(), String.format("MC: setCurrentRoute is setting route to null (NPC id: {})!!!", owner.getNpcId()));
        } else {
            this.currentRoute = currentRoute;
        }
        this.currentPoint = 0;
    }

    public void setRouteStep(RouteStep paramRouteStep1, RouteStep paramRouteStep2) {
        Point2D localPoint2D = null;
        if (((Npc)this.owner).getWalkerGroup() != null) {
            if (((Npc)this.owner).getWalkerGroupShift() == null) {
                log.warn("Missing WalkerGroupShift for: " + ((Npc)this.owner).getNpcId());
                return;
            }
            localPoint2D = WalkerGroup.getLinePoint(new Point2D(paramRouteStep2.getX(), paramRouteStep2.getY()), new Point2D(paramRouteStep1.getX(), paramRouteStep1.getY()), ((Npc)this.owner).getWalkerGroupShift());
            this.pointZ = paramRouteStep2.getZ();
            if (GeoDataConfig.GEO_ENABLE && GeoDataConfig.GEO_NPC_MOVE && !(this.owner.isInFlyingState())) {
                this.pointZ = GeoService.getInstance().getZ(((Creature)this.owner).getWorldId(), paramRouteStep2.getX(), paramRouteStep2.getY(), paramRouteStep2.getZ()-1, 100f, 1);
            }
            ((Npc)this.owner).getWalkerGroup().setStep((Npc)this.owner, paramRouteStep1.getRouteStep());
        } else {
            this.pointZ = paramRouteStep1.getZ();
            if (GeoDataConfig.GEO_ENABLE && GeoDataConfig.GEO_NPC_MOVE && !(this.owner.isInFlyingState())) {
                this.pointZ = GeoService.getInstance().getZ(((Creature)this.owner).getWorldId(), paramRouteStep1.getX(), paramRouteStep1.getY(), paramRouteStep1.getZ()-1, 100f, 1);
            }
        }
        this.currentPoint = paramRouteStep1.getRouteStep() - 1;
        this.pointX = localPoint2D == null ? paramRouteStep1.getX() : localPoint2D.getX();
        this.pointY = localPoint2D == null ? paramRouteStep1.getY() : localPoint2D.getY();
        this.destination = Destination.POINT;
        this.walkPause = paramRouteStep1.getRestTime();
    }

    public int getCurrentPoint() {
        return this.currentPoint;
    }

    public boolean isReachedPoint() {
        return MathUtil.getDistance(((Npc)this.owner).getX(), ((Npc)this.owner).getY(), ((Npc)this.owner).getZ(), this.pointX, this.pointY, this.pointZ) < (double)0.05f;
    }

    public void chooseNextStep() {
        int oldPoint = this.currentPoint;
        if (this.currentRoute == null) {
            WalkerTemplate template;
            WalkManager.stopWalking((NpcAI2)((Npc)this.owner).getAi2());
            if (!WalkerFormator.processClusteredNpc((Npc)this.owner, ((Npc)this.owner).getWorldId(), ((Npc)this.owner).getInstanceId()) && (template = DataManager.WALKER_DATA.getWalkerTemplate(((Npc)this.owner).getSpawn().getWalkerId())) != null) {
                this.currentRoute = template.getRouteSteps();
            }
            if (this.currentRoute == null) {
                log.warn("Bad Walker Id: " + ((Npc)this.owner).getNpcId() + " - point: " + oldPoint);
                return;
            }
        }
        this.currentPoint = this.currentPoint < this.currentRoute.size() - 1 ? ++this.currentPoint : 0;
        this.setRouteStep(this.currentRoute.get(this.currentPoint), this.currentRoute.get(oldPoint));
    }

    public int getWalkPause() {
        return this.walkPause;
    }

    public boolean isChangingDirection() {
        return this.currentPoint == 0;
    }

    @Override
    public final float getTargetX2() {
        return this.started.get() ? this.targetDestX : ((Npc)this.owner).getX();
    }

    @Override
    public final float getTargetY2() {
        return this.started.get() ? this.targetDestY : ((Npc)this.owner).getY();
    }

    @Override
    public final float getTargetZ2() {
        return this.started.get() ? this.targetDestZ : ((Npc)this.owner).getZ();
    }

    public boolean isFollowingTarget() {
        return this.destination == Destination.TARGET_OBJECT;
    }

    public void storeStep() {
        if (((Npc)this.owner).getAi2().getState() == AIState.RETURNING) {
            return;
        }
        if (this.lastSteps == null) {
            this.lastSteps = new LastUsedCache(10);
        }
        Point3D currentStep = new Point3D(((Npc)this.owner).getX(), ((Npc)this.owner).getY(), ((Npc)this.owner).getZ());
        if (((Npc)this.owner).getAi2().isLogging()) {
            AI2Logger.moveinfo((Creature)this.owner, "store back step: X=" + ((Npc)this.owner).getX() + " Y=" + ((Npc)this.owner).getY() + " Z=" + ((Npc)this.owner).getZ());
        }
        if (this.stepSequenceNr == 0 || MathUtil.getDistance(this.lastSteps.get(this.stepSequenceNr), currentStep) >= 5.0) {
            this.stepSequenceNr = (byte)(this.stepSequenceNr + 1);
            this.lastSteps.put(this.stepSequenceNr, currentStep);
        }
    }

    public Point3D recallPreviousStep() {
        Point3D result =  stepSequenceNr == 0 ? null : lastSteps.get(stepSequenceNr--);
        Point3D point3D;
        if (this.lastSteps == null) {
            this.lastSteps = new LastUsedCache(10);
        }
        if (this.stepSequenceNr == 0) {
            point3D = null;
        } else {
            byte by = this.stepSequenceNr;
            this.stepSequenceNr = (byte)(by - 1);
            point3D = result = this.lastSteps.get(by);
        }
        if (result == null) {
            if (((Npc)this.owner).getAi2().isLogging()) {
                AI2Logger.moveinfo((Creature)this.owner, "recall back step: spawn point");
            }
            this.targetDestX = ((Npc)this.owner).getSpawn().getX();
            this.targetDestY = ((Npc)this.owner).getSpawn().getY();
            this.targetDestZ = ((Npc)this.owner).getSpawn().getZ();
            result = new Point3D(this.targetDestX, this.targetDestY, this.targetDestZ);
        } else {
            if (((Npc)this.owner).getAi2().isLogging()) {
                AI2Logger.moveinfo((Creature)this.owner, "recall back step: X=" + result.getX() + " Y=" + result.getY() + " Z=" + result.getZ());
            }
            this.targetDestX = result.getX();
            this.targetDestY = result.getY();
            this.targetDestZ = result.getZ();
        }
        return result;
    }

    public void clearBackSteps() {
        this.stepSequenceNr = 0;
        this.lastSteps = null;
        this.movementMask = 0;
    }

    @Override
    public void skillMovement() {
        // TODO Auto-generated method stub
    }
}

Java:
package com.aionemu.gameserver.ai2.manager;

import com.aionemu.gameserver.ai2.AI2Logger;
import com.aionemu.gameserver.ai2.AISubState;
import com.aionemu.gameserver.ai2.AttackIntention;
import com.aionemu.gameserver.ai2.NpcAI2;
import com.aionemu.gameserver.ai2.event.AIEventType;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.configs.main.GeoDataConfig;
import com.aionemu.gameserver.world.geo.GeoService;
import com.aionemu.gameserver.world.geo.nav.NavData;
import com.aionemu.gameserver.world.geo.nav.NavService;

/**
 * @author ATracer
 * @modified Yon (Aion Reconstruction Project) -- removed non-retail-like leash in {@link #checkGiveupDistance(NpcAI2)}.
 */
public class AttackManager {

    /**
     * @param npcAI
     */
    public static void startAttacking(NpcAI2 npcAI) {
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: startAttacking");
        }
        npcAI.getOwner().getGameStats().setFightStartingTime();
        EmoteManager.emoteStartAttacking(npcAI.getOwner());
        scheduleNextAttack(npcAI);
    }

    /**
     * @param npcAI
     */
    public static void scheduleNextAttack(NpcAI2 npcAI) {
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: scheduleNextAttack");
        }
        // don't start attack while in casting substate
        AISubState subState = npcAI.getSubState();
        if (subState == AISubState.NONE) {
            chooseAttack(npcAI, npcAI.getOwner().getGameStats().getNextAttackInterval());
        } else {
            if (npcAI.isLogging()) {
                AI2Logger.info(npcAI, "Will not choose attack in substate" + subState);
            }
        }
    }

    /**
     * choose attack type
     */
    protected static void chooseAttack(NpcAI2 npcAI, int delay) {
        AttackIntention attackIntention = npcAI.chooseAttackIntention();
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: chooseAttack " + attackIntention + " delay " + delay);
        }
        if (!npcAI.canThink()) {
            return;
        }
        switch (attackIntention) {
        case SIMPLE_ATTACK:
            SimpleAttackManager.performAttack(npcAI, delay);
            break;
        case SKILL_ATTACK:
            SkillAttackManager.performAttack(npcAI, delay);
            break;
        case FINISH_ATTACK:
            npcAI.think();
            break;
        default:
            break;
        }
    }

    /**
     * @param npcAI
     */
    public static void targetTooFar(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: attackTimeDelta " + npc.getGameStats().getLastAttackTimeDelta());
        }

        // switch target if there is more hated creature
        if (npc.getGameStats().getLastChangeTargetTimeDelta() > 5) {
            Creature mostHated = npc.getAggroList().getMostHated();
            if (mostHated != null && !mostHated.getLifeStats().isAlreadyDead()
                    && !npc.isTargeting(mostHated.getObjectId())) {
                if (npcAI.isLogging()) {
                    AI2Logger.info(npcAI, "AttackManager: switching target during chase");
                }
                npcAI.onCreatureEvent(AIEventType.TARGET_CHANGED, mostHated);
                return;
            }
        }
        if (!npc.canSee((Creature) npc.getTarget())) {
            npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
            return;
        }
        if (checkGiveupDistance(npcAI)) {
            npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
            return;
        }
        //Check the NAV navigation on status.
        if (!GeoDataConfig.GEO_NAV_ENABLE) {
            //If the NPC loses its target or vision.
            if (!GeoService.getInstance().canSee(npc,npc.getTarget())) {
            npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
            return;
            }
      
        }
        if (npcAI.isMoveSupported()){
            npc.getMoveController().moveToTargetObject();
            return;
        }
        npcAI.onGeneralEvent(AIEventType.TARGET_GIVEUP);
    }

    private static boolean checkGiveupDistance(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        // if target run away too far
        float distanceToTarget = npc.getDistanceToTarget();
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "AttackManager: distanceToTarget " + distanceToTarget);
        }
        // TODO may be ask AI too
        int chaseTarget = npc.isBoss() ? 200 : npc.getPosition().getWorldMapInstance().getTemplate().getAiInfo().getChaseTarget();
        if (distanceToTarget > chaseTarget) {
            return true;
        }
      
        double distanceToHome = npc.getDistanceToSpawnLocation();
        int chaseHome = npc.isBoss() ? 350 : npc.getPosition().getWorldMapInstance().getTemplate().getAiInfo().getChaseHome();
//        if (distanceToHome > chaseHome) { //Leashes like this don't exist on retail (with very few exceptions)
//            return true;
//        }
        // if npc is far away from home
        // start thinking about home after 100 meters and no attack for 10 seconds (only for default monsters)
        if (chaseHome <= 200 || distanceToHome > chaseHome) { // TODO: Check Client and use chase_user_by_trace value
            if ((npc.getGameStats().getLastAttackTimeDelta() > 10 && npc.getGameStats().getLastAttackedTimeDelta() > 10)
                    /*|| (distanceToHome > chaseHome / 2 && npc.getGameStats().getLastAttackedTimeDelta() > 10)*/) {
                return true;
            }
        }
        return false;
    }
}

Java:
package com.aionemu.gameserver.ai2.handler;

import com.aionemu.gameserver.ai2.AI2Logger;
import com.aionemu.gameserver.ai2.AIState;
import com.aionemu.gameserver.ai2.NpcAI2;
import com.aionemu.gameserver.ai2.manager.EmoteManager;
import com.aionemu.gameserver.ai2.manager.WalkManager;
import com.aionemu.gameserver.model.geometry.Point3D;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.templates.spawns.SpawnTemplate;
import com.aionemu.gameserver.spawnengine.SpawnEngine;
import com.aionemu.gameserver.world.World;
import com.aionemu.gameserver.world.geo.GeoService;
import com.aionemu.gameserver.world.geo.nav.NavService;

import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author ATracer
 */
public class ReturningEventHandler {
    private static final Logger log = LoggerFactory.getLogger(ReturningEventHandler.class);
  
    /**
     * @param npcAI
     */
    public static void onNotAtHome(NpcAI2 npcAI) {
      
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "onNotAtHome");
        }
        if (npcAI.setStateIfNot(AIState.RETURNING)) {
            if (npcAI.isLogging()) {
                AI2Logger.info(npcAI, "returning and restoring");
            }
            EmoteManager.emoteStartReturning(npcAI.getOwner());
        }//Rewriting the Return to Pathfinding Mode method
        if (npcAI.isInState(AIState.RETURNING)) {
            Npc npc = npcAI.getOwner();
            Point3D distination = new Point3D(npc.getSpawn().getX(),npc.getSpawn().getY(),npc.getSpawn().getZ());
            if(distination != null){
               npcAI.getOwner().getMoveController().moveToPoint(distination.getX(),distination.getY(),distination.getZ());
            }
        }
    }
    /**
     * @param npcAI
     */
    //Reordering the onBackHome method
    public static void onBackHome(NpcAI2 npcAI) {
        if (npcAI.isLogging()) {
            AI2Logger.info(npcAI, "onBackHome");
        }
        Npc npc = npcAI.getOwner();
        npc.getMoveController().clearBackSteps();
        if (npcAI.setStateIfNot(AIState.IDLE)) {
            EmoteManager.emoteStartIdling(npcAI.getOwner());
            ThinkEventHandler.thinkIdle(npcAI);
        }
        npc.getController().onReturnHome();
    }
}

GNU Lesser Public License and other information that I didn't include, please fill them in when editing to github once the test works. I really don't know how they are written to fit the format of the project.
If the above code is valid, please give the contribution to my friend qingqing.

The updates and modifications to this project are currently trending quiet, thanks to the many friends who have given and contributed.

My English is not good, so I hope it won't affect your reading. :giggle:
Great contribution! Having npcs return to their spawnpoint using the navmesh makes the emu feel much nicer. However, the code you’ve posted causes the cpu usage to reach 100% because many npcs use “case POINT” for random walking and patrol routes. I’ve changed the code a bit so navmesh code is only called for returning npcs, which brought the cpu usage back to usual. Other than that, everything seems solid, I will test a bit more and if there are no more problems, put the code to github.
 
Newbie Spellweaver
Joined
Mar 7, 2022
Messages
56
Reaction score
100
I've tested the modified code, and my friend has some more code, probably in a different way or method, that he feels could provide some useful help.

222AB - Aion 5.8 emulator improvement community project - RaGEZONE Forums


123A - Aion 5.8 emulator improvement community project - RaGEZONE Forums


123 - Aion 5.8 emulator improvement community project - RaGEZONE Forums

123B - Aion 5.8 emulator improvement community project - RaGEZONE Forums
 

Attachments

You must be registered for see attachments list
Last edited:
Newbie Spellweaver
Joined
Aug 11, 2022
Messages
58
Reaction score
178
Ai returning to spawnpoint using navmesh was added to the navmesh branch several days ago already. It accounts for the cases when npc could get stuck and in general works well. Unless there are obvious benefits to the new code, I don’t really see a reason to add it.
 
Back
Top