As it stands, you're iterating across the whole map and firing anything which rolls against the percentage chance, right?
I have a suggestion to make this a bit more flexible.
Instead of a mapping, I suggest using an array because it retains a fixed order of evaluation. With that, add two additional booleans so each row looks like:
({ function, percentage, iffmode, halt })
iffmode means "only try to execute this if you executed the previous entry", and halt means "if you execute this, stop here"
Then you could set up pretty complex scenarios like the following.
({
({ (: UnlockDoor :), 50, 0, 0 }),
({ (: OpenDoor :), 50, 1, 0 }),
({ (: ExclaimHooray :), 100, 1, 1 }),
({ (: BangOnDoor :), 50, 0, 0 }),
({ (: Grumble :), 50, 1, 1 })
})
In this case, your NPC has a 50% chance to unlock some door. If it does so, it then has another 50% chance to open it. If it does THAT, it will then emote something about how it's happy to finally get through the door, and we're done.
If we didn't actually open the door, the NPC has a 50% chance to bang on it, and if it does so, it may then grumble about the door being closed.
You can, of course, get the previous behavior by just always leaving iffmode and halt false, the only difference being that things will always happen in the same order rather than "semi-randomly" based on the order keys() returns.
I saw DikuMUD in there and it reminded me of the way our area resets work, and how useful that "iff" clause is on occasion.