diff --git a/docs/how/targeting.rst b/docs/how/targeting.rst new file mode 100644 index 0000000..53d4055 --- /dev/null +++ b/docs/how/targeting.rst @@ -0,0 +1,96 @@ +======================= +target resolution +======================= + +how we find what the player is talking about. + +the problem +=========== + +when a player types "attack goblin" or "get sword", we need to figure out +which goblin, which sword. could be multiple matches. could be an alias. +could be "2.goblin" to mean the second one. + +the system uses graceful degradation: try exact matches, fall back to prefix +matches, then aliases, then ordinal selection. + +parsing the raw input +===================== + +``parse_target(raw)`` extracts ordinal prefixes:: + + "goblin" → (1, "goblin") + "2.goblin" → (2, "goblin") + "3.rat" → (3, "rat") + +only ordinals >= 1 are valid. "0.goblin" or "-1.goblin" are treated as literal +names (weird, but no crash). + +matching priority +================= + +``resolve_target(name, candidates, key=None)`` uses a priority-based matching +flow: + +**four match priorities** (checked in order, stops at first non-empty result): + +1. exact name match (case-insensitive) +2. name prefix match ("gob" matches "goblin") +3. exact alias match +4. alias prefix match ("gobb" matches alias "gobby") + +collects ALL matches at each priority level before moving to the next. this +prevents a prefix match from shadowing a later exact alias match. + +**ordinal selection**: if an ordinal was parsed (e.g., "2.goblin"), picks the +Nth match from whichever priority level produced results. + +**fallback**: returns None if no matches found at any level. + +custom key function lets you adapt to non-standard objects (e.g., dicts with +a "name" field). + +finding entities +================ + +``find_entity_on_tile(name, player, z_filter=True)`` searches players and mobs +on the same tile. + +filters: +- excludes the player themselves +- skips dead mobs +- z-axis filtering: only matches entities on same z-level (both grounded or + both flying). flying creatures can't attack grounded ones by default. + +sorts results to prefer Players over Mobs. + +finding things +============== + +``find_thing_on_tile(name, zone, x, y)`` searches ground items at coordinates. +only Thing instances. + +``find_in_inventory(name, player)`` searches player inventory. only Thing +instances. + +ordinal disambiguation +====================== + +if there are three goblins, you can target the second with "2.goblin":: + + attack 2.goblin + +the ordinal is parsed, then resolve_target collects matches and picks the Nth +one. ordinal selection happens after all four priority levels have been tried, +operating on whichever level produced matches. + +case sensitivity +================ + +all matching is case-insensitive. "Goblin", "goblin", "GOBLIN" all work. + +code +==== + +- ``src/mudlib/commands/targeting.py`` - parse and resolve functions +- ``tests/test_targeting.py`` - test coverage for all priority levels