Skip to content

Object resolvers update

Summary: This updates object resolvers, eliminating redundant code to ensure consistent behaviour and integrating them into the debugging object creation interface -- allowing resolver filters to be debugged directly within the debugging dialogs.

It adds a new system for NPCs choosing equipment to wear based on filters, and updates ToME character class descriptors and Entity definitions to ensure only appropriate equipment is worn (to prevent wearing Objects that interfere with talents). This fixes the issue of NPCs getting added random classes with many unusable talents. (Embers and Ashes class descriptors and Entities work as before, but need to be updated to benefit from the changes.)

Details:

New utility functions (useful for debugging): string.fromFunction(fct, fmt) returns a string abbreviation for a function, including filepath, line numbers

string.fromValue(v, recurse, offset, prefix, suffix) -- enhancement of tostring function with more thorough handling of tables and functions

string.fromTable(src, recurse, offset, prefix, suffix, key_recurse): returns a single-line string representation of a table, including string representations of subtables and functions. The recursion level for both keys and values can be set. The converted string is lua source code compatible, depending on arguments.

engine.Zone:checkFilter: Added documentation, can set filter.allow_uniques to allow unique items to pass the filter, special function takes the filter as its 2nd argument

engine.Zone:filterToString: updated -- handles subtables, functions, and other complex values

engine.Zone:makeEntity: the type parameter can be a list of entities

engine.Zone:makeEntityByName: Added documentation, type can be a string descriptor used to load a base list (follows makeEntity filter.base_list)

engine.Entity:resolve: Added documentation

engine.Birther: Bugfix: the order of resolvers is preserved when applying birth descriptors (causes resolver.attachtinker to fail with no equipment to attach to)

engine.interface.ActorInventory:initBody() May be reapplied to an actor without overwriting existing inventories (merges properties), copies the inventory definition short_name

dialogs.debug.SummonCreature, dialogs.debug.RandomActor: Actors are fully resolved before added: including "addedToLevel", "on_added", "on_added_to_level" (except spawning escorts) updating for game difficulty, etc. on generation, before adding Uniques (including inventory items) are not registered in the game until the actor is actually placed.

dialogs.debug:RandomObject: Can set the working actor to be a NPC (defaults to the player) New button/hotkeys to open the working actor character sheet and inventory screens Can select from one of several object resolvers, which will call the appropriate code and resolve the object. This works like other object generation, allowing preview/inspection before adding the object to the game, which is controlled by the selected resolver as normal Uniques created are not registered until actually added to the game.

CharacterSheet: can always open the inventory dialog in debug mode

Actor:resolveLevelTalents: code to apply self.learn_tids moved from createRandomBoss to this function (enabling this function for all Actors)

New function Object:wornLocations(use_actor, weight_fn, filter_field, no_type_check) (This functionality replaces the no_npc_weapon_equip flag.) Returns a table if inventory locations where an object can be worn, filtered and sorted The "best" locations appear first, permitting Actors to automatically optimize their equipment. The default criteria are simple (based on object "power", from material level and getPowerRank), but can be updated to be more sophisticated.

New function Actor:wearAllInventory(force, ...) Attempts to wear all items in main inventory, using Object:wornLocations to find the best locations (currently does not attempt to attach tinkers, aggressively remove worn items, or support psionic focus)

New function Actor:updateObjectRequirements(o) Returns object requirements modified by actor talents Used by Actor:canWearObject, Object:getRequirementDesc, and resolvers.resolveObject

New function Actor:searchAllInventories(o, fct) Searches all inventories for an object (including attached tinkers). Triggers an optional callback on found items.

NPC:addedToLevel: classes added from higher difficulty settings allow equipment to be updated and autolevel stats (if more than one class added) calls Actor:wearAllInventory to equip objects

game.state:entityFilterAlter: Added documentation, updated RandomObject help

game.state:entityFilter: Added documentation, updated RandomObject help

game.state:entityFilterPost: Added documentation, updated RandomObject and RandomActor help

game.state:egoFilter: Added documentation

game.state:applyRandomClass: Fixed a bug causing starting class stats to not be added properly Copies all class resolvers with _allow_random_boss set true (resolvers.equip: ignore_material_restriction=true) Resolvers enabled: inscription, inscriptions, auto_equip_filters, attach_tinker, inventory, drops, drop_randart The can_tinker table is copied (allows resolvers.attach_tinker to work properly) autolevel set to "random_boss" unless data.autolevel == false May set data.update_body to force adding inventory slots to the actor May set data.spend_points to spend all unspent stat points data.force_classes can take numerical indexes. Fixed a bug causing multiple forced classes to be applied in definition order rather than randomly. (Example: {"Rogue", Alchemist=true, Necromancer=true} always adds the Rogue class first then Alchemist and Necromancer in random order, before adding any other random classes)

game.state:createRandomBoss: Allows learn_tids to be applied by resolveLevelTalents (random class talents are learned before attempting to wear equipment with NPC:addedToLevel) No longer removes equip and drops resolvers from the base NPC This preserves important drops and equipment (updated to "boss" levels) required for NPC talents (i.e. randelite Naga Mymidons are not spawned disarmed/improperly armed with certain classes) -- applyRandomClass replaces equipment and updates inventory filters as needed.

New resolver -- resolvers.auto_equip_filters(t, readonly): Sets filters by inventory slot to be checked before automatically wearing objects. Can be used by class descriptors and general npc definitions to ensure that equipment automatically equipped will not interfere with talents. Input is either a table of filters or a class descriptor name to load the filters from the referenced birth descriptors (i.e. resolvers.auto_equip_filters("Archmage")). As usual, multiple copies of this resolver overwrite each other by inventory slot (use readonly to prevent), so randbosses with more than one class will generally use the filters for the last class applied. (i.e. Alchemist (applied first), Rogue (applied second) will preferentially equip daggers in MAINHAND/OFFHAND and alchemist gems in QUIVER)

New general object resolver function -- resolvers.resolveObject(e, filter, do_wear, tries): Called by all filter-based object resolvers for consistency, interprets the filter fields: base_list, random_art_replace, alter (takes the resolving actor as an argument), autoreq (can trigger actor levelup and learning talents in addition to stats), force_inven, force_item, force_drop, never_drop, _use_object (sets a pre-resolved object to use, skipping random generation), replace_unique (filter for generating a replacement for a specific object) Uses Object:wornLocations to find the best locations in which to wear objects. Correctly handles replaced objects and always places unworn objects into main inventory unless explicitly forbidden. (Calls game.zone:addObject only for objects that are actually added to the actor.) Documentation serves as core help in RandomObject dialog for resolvers.

resolvers.equip: updated to use resolvers.resolveObject

resolvers.inventory: updated to use resolvers.resolveObject

resolvers.drops: updated to use resolvers.resolveObject

resolvers.attachtinker: updated to use resolvers.resolveObject, will place the tinker in main inventory if .keep_object is set

resolvers.drop_randart: interprets .data field as input to game.state:generateRandart, may set ._use_object to use a pre-resolved object and .no_add to return the generated object instead of adding it to inventory

Class Descriptors updated with inventory filters: Spellpower based casters (Anorithil, Paradox Mage, Archmage, Alchemist, Necromancer, Corruptor): MAINHAND: staff OFFHAND: none unless a 1H weapon is equipped in MAINHAND Alchemist: QUIVER: alchemist-gem

Oozemancer, Summoner, Doomed: MAINHAND/OFFHAND: mindstars

Stone Warden: MAINHAND/OFFHAND: shield or weapon with the block combat field

Brawler: MAINHAND/OFFHAND: unarmed

Reaver: MAINHAND/OFFHAND: 1H weapon

Archer: MAINHAND: Archery weapon QUIVER: Ammo must match type of MAINHAND weapon

Temporal Warden: MAINHAND: longbow OFFHAND: none QUIVER: Ammo must match type of MAINHAND weapon QS_MAINHAND: 1H weapon

Sun Paladin, Bullwark: MAINHAND: 1H weapon OFFHAND: shield or weapon with the block combat field BODY: heavy or massive armor

Berzerker: MAINHAND: 2H weapon OFFHAND: unarmed

Rogue, Shadowblade, Marauder: MAINHAND/OFFHAND: dagger Rogue, Shadowblade: BODY: light or cloth armor

Skirmisher: MAINHAND: sling OFFHAND: shield or weapon with the block combat field QUIVER: Ammo must match type of MAINHAND weapon

Some NPC Entity updates: Bosses in Trollmire, The Maze, Daikara, Dreadfell, Elven Ruins, High Peak, Orc Prides, Assassin Lord, Tannen, Golbug, Corrupted Oozemancer, Some major and minor demons, various caster npcs, many orcs, thieves, minotaurs, yaechs, naga

Merge request reports