Your list includes several things which should have their tracking abstracted separately, rather than pooled into a single global pile of state.
For example, quest completion is something that all quests will track. Similarly, all dungeons will likely need to have their completion status tracked. However, dungeons are generally a binary value (you completed it, or you didn't) whereas quests might not be (you may have finished it, but did you succeed or fail?). So while you might have functions like DidFinishDungeon(dungeon)
and DidFinishQuest(quest)
, the former will probably just return a Boolean value while the latter could very well return some kind of QuestResult
object.
By way of contrast, you probably won't want to track whether or not every NPC has or has not been talked to. That's generally handed by setting and checking scripting flags within the dialog or script system.
You'll probably want to track chest opening, at least for special chests with unique rewards, but you'll probably want to store that data in a dedicated block of character data representing chest-recovery state (or perhaps tie it to dungeon completion state, if your game mechanics allow so).
Underneath the abstraction, you can store all the Boolean values for a character together in a single blob of storage, using "progress bits." This sort of storage is basically an array of integer values used to represent a collection of bits; you assign bit indices to particular events (such as "talked to Farmer Bob") and provide a way to expose the checking and setting of those bits to dialog or scripting systems in your game. You'll also want a want to recycle the indices of bits you remove or repurpose, so you don't leave holes in the address space; good toolset support really helps here, although if you game is small enough in scope you can simply track this manually in a spreadsheet or something.
It generally makes the most sense to use this system for more freeform "player has done stuff" flags, like talking to NPCs, reaching certain parts of a level to trigger an event, et cetera. One-off things for which you only need to store a true/false value. In the interest of reducing dependencies and keeping the progress bit space confined, I'd store things like quest/dungeon completion and chest-opening data elsewhere, in a section dedicated to that storage.
Achievement-like data ("kill 30 goblins") is similarly better off stored with or by the achievement itself, because it likely varies so wildly between achievements and often requires more than a single bit to store progress towards the achievement. You could also extend the progress bit system to allow for both toggle bits and range bits, since you are only really limited in storage by the memory/storage limits you impose on yourself for you game.