-- categories.lua — classifies map contents into tracker categories. -- -- The game's own `templatesByTag` defines exactly one tag ("exit"), so the -- tracker needs its own scheme. Per docs/05-decisions.md D4 this is deliberately -- FLAG-FIRST rather than a hand-listed set of template IDs: -- -- * flags are authoritative -- modules/tile.lua:234 defines -- `tile.tryInteract = (loot or tryInteractFunction) and ...`, which is -- exactly "the player can do something here" versus scenery; -- * new content in a future game version falls through to a sensible category -- instead of vanishing from the tracker. -- -- The only hand-maintained parts are the exclusion list and the exit tag reuse. local categories = {} -------------------------------------------------------------------------------- -- Exclusions -------------------------------------------------------------------------------- -- The six templates with no name, no description and no parent: invisible -- mechanics that must never be tracked. (See docs/05-decisions.md D4.) local excludedTemplates = { blank = true, throw_blocker = true, movement_blocker = true, fire_detector = true, wall_features_position_dummy = true, ladder_down_hidden = true, } -------------------------------------------------------------------------------- -- Category list (order is the Ctrl+PageUp/PageDown cycle order) -------------------------------------------------------------------------------- categories.list = { {key = "creatures", name = "Creatures"}, {key = "items", name = "Items"}, {key = "alchemy", name = "Alchemy"}, {key = "interactables", name = "Interactables"}, {key = "exits", name = "Exits"}, {key = "terrain", name = "Terrain features"}, } -------------------------------------------------------------------------------- -- Helpers -------------------------------------------------------------------------------- local exitTemplates local function getExitTemplates() if exitTemplates then return exitTemplates end local ok, templatesByTag = pcall(require, "modules.tile.templatesByTag") exitTemplates = (ok and templatesByTag and templatesByTag.exit) or {} return exitTemplates end local itemTemplates local function getItemTemplates() if itemTemplates then return itemTemplates end local ok, templates = pcall(require, "modules.item.templates") itemTemplates = (ok and templates) or {} return itemTemplates end -- Item category 3 is "Ingredients" (modules/item/categories.lua). local INGREDIENT_CATEGORY = 3 local function lootContainsIngredient(loot) if type(loot) ~= "table" then return false end local templates = getItemTemplates() for _, entry in ipairs(loot) do local templateId = type(entry) == "table" and entry.templateId local template = templateId and templates[templateId] if template and template.category == INGREDIENT_CATEGORY then return true end end return false end local function isLootable(tile) return tile.loot and tile.loot[1] and not tile.unlootable end -------------------------------------------------------------------------------- -- Classification -------------------------------------------------------------------------------- -- Returns a category key, or nil if the tile should not be tracked. function categories.classifyTile(tile) if not tile or tile.isHidden or not tile.templateId then return nil end if excludedTemplates[tile.templateId] then return nil end if getExitTemplates()[tile.templateId] then return "exits" end if isLootable(tile) then return lootContainsIngredient(tile.loot) and "alchemy" or "items" end -- Structural tiles are never tracked, even though many of them report -- tryInteract. -- -- `wall` carries a flavour-only tryInteractFunction ("You touch the wall. -- It's cold and rough."), so the flag-first rule promoted every wall segment -- into Interactables -- 13 of them in the starting house alone, burying the -- chest and the cauldron. tryInteract means "something happens if you press -- interact", NOT "this is worth walking to". -- -- blocksMovement AND blocksVision identifies the structural shell (walls, -- solid rock). Containers and furniture block movement but not sight, so they -- survive this filter. Walls are covered by the wall tones and the -- surroundings scan anyway. if tile.blocksMovement and tile.blocksVision then return nil end if tile.tryInteract then return "interactables" end -- Terrain is intentionally narrow: hazards and water only. Including every -- wall would bury the useful entries under hundreds of identical ones -- and -- walls are already covered by the wall tones and the surroundings scan. if tile.isThreatening then return "terrain" end return nil end -- Water is a property of the level's water map rather than of a tile, so it is -- classified separately during the scan. categories.WATER_CATEGORY = "terrain" function categories.tileName(tile) return tile.name or tile.templateId or "something" end function categories.actorName(actor) return actor.nameObserved or actor.name or actor.templateId or "someone" end -- Kind key: groups instances of the same thing together. Template id is stabler -- than the display name (two templates can share a name). function categories.tileKind(tile) return tile.templateId end function categories.actorKind(actor) return actor.templateIdOnExamination or actor.templateId end return categories