Skip to content



Akkadius (KLS)

NPC Movement Overhaul


"Recently, we landed massive changes to pathing logic (Navmesh) with NPC's that took a while to iron out but has been an immensely positive addition to the EQEmu server core. With that, it's made us realize how much some related functionality needed to be addressed and that involves "NPC movement logic". We've been needing to address NPC movement logic for years as it has been fragmented and inconsistent all over the code which has made fixing actual movement related issues a nightmare. There are many other technical things that we can go into about it but the important part is that it is much improved! Special thanks to KLS for putting in a lot of work to make this happen"

NPC Behavioral Changes

  • NPC's should now smoothly path between source and destination, rather than "skipping" all over the place between path points and sending sometimes several position updates, there should only be one update until NPC changes heading or speed
  • NPC pathing during flee and fear should be far improved
  • NPC pathing inside of water should be far improved
  • NPC transitions between water and land should now be seamless
  • NPC transitions over verticals (hills) should be smoother as well
  • NPC "Stuck" logic during path finding should be improved
  • NPC Z coordinate plane correction now occurs during path calculation projection properly (as it should) instead of brute forcing the current Z location along different path projections

Example of Z-plane Correction



  • Implemented: Global Base Scaling:
  • Implemented: Beginning of DevTools * more to be added over time (Status >= 200)
  • Display NPC info on target
  • Display Client info on target
  • Display NPC show commands on target
    • Loot
    • Grids
    • Emotes
  • Implemented: Support for saylinks to be used in GM commands
  • Deprecated: Commands
  • listnpcs
  • Implemented: Commands
  • list [npcs.|players.|corpses|doors|objects] [search]
    • Provides clickable saylinks to go to entities
  • scale [static/dynamic] (With targeted NPC)
  • scale [npc_name_search] [static/dynamic] (To make zone-wide changes)
  • scale all [static/dynamic]
  • devtools (alias #dev)
  • Implemented: New Logging categories automatically injected into logsys_categories table
  • Fixed an issue where newly injected categories were turned on by default



# Allows you to pull the instance ID of a client by character ID
quest::GetInstanceIDByCharID(const char *zone, int16 version, uint32 char_id);

# Allows you to assign an instance to a client by character ID
quest::AssignToInstanceByCharID(uint16 instance_id, uint32 char_id);

# Allows you to remove a client from an instance by character ID
quest::RemoveFromInstanceByCharID(uint16 instance_id, uint32 char_id);

Added spell buckets, similar to spell globals

  • Uses a new spell_buckets table and the Spells:EnableSpellBuckets rule. Added max level by data bucket.
  • Uses data bucket char_id-CharMaxLevel and Character:PerCharacterBucketMaxLevel rule



Added bot owner options

  • Usage: ^owneroption [option] (or aliased as: ^oo [option])
  • Options are saved in the database and therefore, persistent
  • Implemented option 'deathmarquee'
  • Toggles client owner flag to show marquee message when a bot dies (default: disabled)



Fixed a few bot issues

  • Fix for bot item trades not attuning
  • Fix for bot mana, endurance not updating properly



Fixed a Few Bot Issues

  • Fix for bot 'stop melee level' not honoring setting level over rule level
  • Fix for missing bot combat spell casting when within melee range
  • Fix (in-work) for bots 'forgetting' current target when it flees



Notes for manual conversion of quest script inventory slot values

You should use reference/lookup values provided by the lua and perl apis to avoid skirting safety checks and to ensure that the values used are the correct ones for your needs

my $charmitem = $client->GetItemIDAt(0);
for($for_x = 22; $for_x < 30; $for_x++) {...}
for($slot1 = 0; $slot1 <= 30; $slot1++) {...}
my $charmitem = $client->GetItemIDAt(quest::getinventoryslotid("charm"));

# General Inventory
my $slot_begin = quest::getinventoryslotid("general.begin");
my $slot_end = quest::getinventoryslotid("general.end");
for($for_x = $slot_begin; $for_x <= $slot_end; $for_x++) {}

# Possessions 
my $slot_begin = quest::getinventoryslotid("possessions.begin");
my $slot_end = quest::getinventoryslotid("possessions.end");
for($for_x = $slot_begin; $for_x <= $slot_end; $for_x++) {}



Re-work of #invsnapshot Command and Implementation of Automatic Inventory Snapshots

  • Inventory snapshots are now taken automatically using the interval rule values - if snapshots are enabled
  • Command invsnapshot now has more options available to include a restore feature
  • A pop-up help menu is available
  • Argument 'capture' is available to anyone with status high enough to register the command
  • Advanced options are only available to players with 150 status or greater
  • Argument 'list' provides a list of "timestamp : item count" entries
  • Argument 'parse' displays a "slot : item id : item name" listing of valid snapshots by timestamp
  • Argument 'compare' shows a 'difference' comparison of "snapshot-to-inventory" changes
  • Argument 'restore' applies a saved snapshot to the player's inventory (with a pre-clearing call)



Activation of RoF+ Clients Two Additional General Slots and Integration of SoF+ Clients PowerSource Slot

  • Inventory 'Possessions' main slots are now contiguous and implemented to RoF2 standards
  • slotGeneral9(31) and slotGeneral10 (32) are now active
  • Possessions slot enumerations are now defined as between slotCharm (0) and slotCursor (33)
  • slotPowerSource (21) is no longer a special case slot (9999)
  • Special code has been added to exclude 'slotPowerSource,' 'slotGeneral9' and 'slotGeneral10' from server actions in clients that do not support them.
  • The LUA Quest API has been updated to use the new slot enumeration as well as having some slot range definitions added
  • The Perl Quest API now has a look-up function to provide token-to-value translations, definitions are similar to LUA
  • In both LUA and Perl, it is HIGHLY recommended that any custom scripts using hard-coded inventory slot values be updated to use the constants/lookup methods as any old values are no longer guaranteed to be accurate
  • Database will have existing inventory slot values modified to the new standard and table items entries will also be updated to the 'correct' equipable slot bitmask
  • Script (quest) updates are required with this change


The proper way to reference inventory slots is to use either intrinsic lookups (c/c++ & perl) or to use valid const ref declarations (c/c++ & lua). Any other method is not guaranteed to be accurate and may result in item loss and/or unexpected/undefined behavior



Adjusted DataBuckets to Use Other Acceptable Time Formats

quest::set_data('key', 'value', '1d');

Databucket Time Input Examples

Input Value
15s 15 seconds
s15 15 seconds
60m 60 minutes
7d 7 days
1y 1 year
600 600 seconds



Rework of Task System - Shared Tasks Still Unsupported

  • The tables now have better named columns, which hopefully won't need to be explained
  • Text1 is now target_name, Text2 is now item_list, Text3 is now description_override
  • Tasks can now reward faction hits, this is just normal NPC Faction ID entries because I was lazy
  • The "Task" type tasks are now supported, a player can only be doing one of these at a time
  • Activity IDs for use spell on and use skill on have been identified but not implemented
  • Identified "duration code" for unlimited duration tasks (None = 0, Short = 1, Medium = 2, Long = 3)
  • Activities can now have multiple zones separated by ';'
  • tasks.startzone removed since it actually just uses first activity
  • skill_list and spell_list are both IDs that can be separated by ';' (still unimplemented though)



Implemented a MUCH Better Replacement for "qglobals" called "DataBuckets"



Reintegration of Inventory-Based EQDictionary References

  • Standardized 'CONSTANT_DECLARATION' and 'enumerationValue' tokens for most of the affected references
  • Added 'BEGIN' and 'END' constants to many inventory-based ranges to help eliminate '< SIZE'-type comparisons
  • Eliminated multiple, duplicated reference points of the same value context (bye, bye namespace legacy!)
  • Most server values are now linked to the implementation client directly through a 'using ##' directive



Lots of Changes! Maps, Pathing, Zone Shutdown, Aggro Distances

  • Fixed an issue where size 0 NPC's hop in and out of the ground at idle
  • NPC's now open doors within proximity given the door doesn't have locked requirements
  • #reloadallrules will now display zones that have had their rules reloaded to GM's
  • Zones should now respect "shutdowndelay" in the zones table at all times
  • Fixed an issue where boats would snap to the bed of the body of water
  • Added rule Aggro:NPCAggroMaxDistanceEnabled * defaults to true
  • Changed map directory load structure to
  • maps/base/*.map
  • maps/nav/*.nav
  • maps/water/*.wtr
  • maps/path/*.path


Implemented navigation mesh

  • All up to date maps and navs can be found
  • To update maps either check out the maps found in the above repository or use the maintenance command: perl maps



Spell Changes to Fade Effect

SE_CastOnFadeEffect, SE_CastOnFadeEffectNPC, SE_CastOnFadeEffectAlway triggered spell will now hit the correct targets.



Added GM Command #ucs to Force a Reconnect to UCS Server

  • Works in place of client auto-reconnect packet in zones where feature is unsupported
  • Currently, you will need to manually re-join channels



Updated UCS Versioning

  • SoF and higher clients have a new opcode identified (update your *.conf files)
  • Rework of previous ucs connectivity code
  • Unrelated: Zone::weatherSend() now takes an optional parameter for singular updates (as in client entering zone) - prior to this, every client already in-zone received a weather update packet whenever a new client zoned in



Bug Reporting Fix and Overhaul

  • Fixed bug reporting for SoD+ clients
  • Added ability to disable bug reporting (set rule 'Bugs:ReportingSystemActive' to 'false')
  • Implemented a more detailed reporting system (set rule 'Bugs:UseOldReportingMethod' to 'false') - New system is not currently compatible with script-based monitoring
  • Soft-removal of defunct 'Petition Bug' system



Fix Heading - Quests broken

Please report any other issues with heading, most things were tested and worked

You can use to run a conversion to fix your headings in quests. Some may need manual review.



Add Global Loot system

This will allow us to implement global loot similarly to how it works on live This system reuses our current loottable tables which the global_loot table references. The limits for the rules to govern if a table should be rolled are min level, max level, rare, raid, race, class, bodytype, and zone race, class, bodytype, and zone are a pipe | separated list of IDs.



Re-Work of Bot::AI_Process()

  • Removed a 'ton' of unneeded packet updates
  • Added a 'leash' to the distance a bot can travel
  • Added a 'main assist' feature to target control (set using group roles)
  • Added combat 'jitter' movement to complement the existing rogue movement
  • Attack can now be aborted if target contains no leash owner nor bot hate and leash owner turns off auto-attack
  • Please report any issues with the bot AI code

Added a work-around for heal rotations crashing the server - under certain conditions.



Spell AI Tweaks

AI spells are treated as "innate" spells (devs use this term, and I think this is what they mean by it) These spells are spammed by the NPC, lots of encounters on live work like this and this will greatly reduce the need to do quest scripting on these types of encounters.

You can safely run update npc_spells_entries set priority = priority + 1 where priority >= 0; if you want to disable this new behavior