Witnesses and rumour
Who saw what. Record an act, let the engine decide who witnessed it, and read that knowledge from any mod - the police, the family, a blackmailer.
A crime, an affair, a fight: what makes it matter is who knows. The Witness global keeps that, once, for every mod. Your mod says what happened; the engine, which is the only one that knows who is standing where, decides who saw it. Everybody else reads the same record: the cousin who caught something in your mod exists for the police mod, the family mod and the blackmailer too.
Recording an act
lualocal id = Witness:act("my_mod", "theft", {
actors = { Player }, -- who did it
targets = { Actor }, -- who it was done to (optional)
discreet = true, -- they tried to hide it: harder to see, behind a door at home
unseen = true, -- nobody is rolled: a secret from before the story (name who knows with Witness:learn)
severity = 3, -- optional, else the act type's own
data = { where = "till" }, -- strings for readers (optional)
})
The engine rolls everyone present: people in the room can see it, people elsewhere in the building can hear it, and the act type decides how visible and how loud it is. A busy bar at 22:00 has many witnesses; a park at 3 a.m. usually none. The participants always know. id is nil on a bad call (unknown mod, no actor).
Some acts are recorded without any call: every sex scene the engine runs checks its participants, and records incest between relatives, an affair when someone has a partner who is not in the scene, and a sexual assault when the scene names who forces it with Game:TalkNonConsensual(forcer) (Sex scenes). The autonomous town's own thefts, shakedowns and fights are recorded too, so the player can witness them. In a player scene, Actor:animate("fightlost") records a fight: the loser is the target, the people standing within a few metres of them are the fighters (once per loser per scene). A fight your scene only narrates, with nobody beside the loser, records nothing: call Witness:act yourself, and use assault when it was one-sided.
Asking who knows
| Call | Returns |
|---|---|
Witness:knows(person, id) |
false, or "saw", "heard", "suspects", "told" |
Witness:knowers(id) |
every row: { person, certainty, source, hours, silence, id }, participants included |
Witness:knownBy(person [, { type = "affair", about = someone }]) |
what this person knows, optionally of one type and about someone |
Witness:isKnown(id) |
true when anyone besides the participants knows |
Witness:get(id) |
the act: type, actors, targets (ids), place, hours, severity, discreet, data, and the type's report, gossip, sexual rates |
The player is not told who saw them. That is narration, and yours to write: a look across the bar, a message the next day, a police officer at the door.
Acting on knowledge
Witness:tell(teller, listener, id): a confession, a denunciation, gossip. The listener knows it as"told", from the teller, only if the teller knew.Witness:learn(person, id, certainty [, source]): your mod's own discovery (the footage, a letter).Witness:silence(person, id, "asked" | "paid" | "threatened" | "compromised"): kept quiet. They still know; a paid witness can still talk one day.
Following up, once
A reader (a police mod, a family mod) takes each piece of knowledge once, the way Outcomes work:
luafor _, k in ipairs(Witness:followUps("my_mod", { "theft", "assault" })) do
if Witness:handle("my_mod", k.id) then
-- k.person knows k.act, with k.certainty
end
end
followUps never returns what the participants themselves know.
The police: ul_Warrant
ul_Warrant is the law's reader. Report a crime with Actor:commit instead of Actor:crime, and it goes through the witnesses:
luaPlayer:commit("my_mod", "robbery", 3, { targets = { Actor } })
Seen by someone who was not part of it: each person who saw rolls on how bad it looked (about 27% for a severity-1 act up to 75% for severity 5, so a crowded place is riskier) and the stars land now if one of them calls. A bystander who does not call is still on record: like the victim and anyone who heard or suspects something, they may go to the police in the days after, each once, at the act type's report rate (less for hearsay, rarely once silenced). One act is one charge, however many people saw it.
The player can go and talk to someone who saw (the interaction Talk about what they saw, from ul_Warrant, offered for a person who saw or heard an act of severity 2 or more that has not reached the police yet): ask them, pay them, or threaten them. A success is Witness:silence; a failure costs rapport, and a failed threat can be reported at once. Warrant.witnessOf(person) returns the knowledge row the interaction is built on. Actor:crime(stars) still works for the police catching the player themselves.
When your scene has already put the police in it (a caution at the station, a patrol car on their tail, a camera they will pull), use Actor:charged with the same arguments: the act goes on record and is charged at once, so nobody who saw it can take it to the police a second time.
luaPlayer:charged("my_mod", "vandalism", 1)
If your mod records the act itself with Witness:act and decides on its own that it reached the police, call Warrant.chargeAct(Player, actId): it charges the act once and returns false if it was charged already. Put the stars in the act's data as ul_warrant_stars (a string), or the act type's severity is used. Someone who only hears of an act after it was charged (gossip, Witness:tell) never charges it again.
Act types are mod data
Content/Modules/<mod>/Witness/*.ulwitness, merged across mods, a later mod's entry replacing an earlier one with the same name:
json{
"acts": {
"robbery": { "visibility": 0.6, "noise": 0.45, "severity": 3, "gossip": 0.45, "report": 0.6 },
"pickpocket": { "visibility": 0.2, "noise": 0.0, "severity": 1, "gossip": 0.15, "report": 0.35, "fadeDays": 60 }
}
}
visibility and noise are the chances of being seen in the room and heard nearby, severity 1-5, report how readily a knower goes to the police, fadeDays when knowledge of a minor act is forgotten, sexual: true caps what a minor can know at "heard". ul_Base ships the common ones (theft, assault, murder, drug_deal, drink_spiking, affair, incest, sexual_assault, blackmail, extortion...).
In NPC scenes
An NPC scene has Witness too. act, tell and silence are recorded when the scene succeeds, at the NPC's own place and hour, so a refused scene leaves no witness of something that never happened; act therefore returns nil there. knows, isKnown and knownBy read the live records, and knownBy rows also carry who (the first actor's id, for Game:getPerson) and role ("actor" or "target" for the about person): a blackmailer wants the one who did it, never the victim. See ul_TownSecrets for blackmail, gossip and a partner confronting an affair, all built on what someone really saw.