Locks and doors
This page uses GameObject from Unity. The glossary says what each one is.
A lock keeps a door, chest or other object shut until it is opened with a key, a pick or a code. The target for these calls is any object with a game lock on it, such as a door, a storage, a button, or a keypad.
Locks reading, changing and picking locks and keypads
This is the class you start from. It reads and changes locks and keypads, and StartLockpick runs the lockpick minigame. Its Locked, Unlocked and lockpick events give you a LockArgs.
public static class Locks
{
public static event Action<LockArgs> Locked;
public static event Action<LockArgs> Unlocked;
public static event Action<LockArgs> LockpickSucceeded;
public static event Action<LockArgs> LockpickFailed;
public static bool IsAvailable { get; }
public static bool IsLocked(GameObject target);
public static bool IsLockpickable(GameObject target);
public static float Difficulty(GameObject target);
public static bool PlayerHasKey(GameObject target);
public static bool Lock(GameObject target);
public static bool Unlock(GameObject target);
public static bool SetLockpickable(GameObject target, bool lockpickable);
public static bool SetDifficulty(GameObject target, float difficulty);
public static bool SetKeypadOpen(GameObject target, bool open);
public static bool LockKeypad(GameObject target);
public static bool TriggerKeypad(GameObject target, bool success);
public static int[] KeypadCode(string id);
public static bool SetKeypadCode(string id, int[] code);
public static bool StartLockpick(GameObject target, float difficulty, Action<bool> done);
}
| Method | Returns | What it does |
|---|---|---|
IsLocked(target) | bool | true when the object is locked. |
IsLockpickable(target) | bool | true when the lock can be picked. |
Difficulty(target) | float | How hard it is to pick, from 0 to 2. It is -1 when the object has no lock. |
PlayerHasKey(target) | bool | true when the player carries the key. |
Lock(target) | bool | Locks it. It is true when it is locked afterwards. |
Unlock(target) | bool | Unlocks it. It is true when it is unlocked afterwards. |
SetLockpickable(target, lockpickable) | bool | Lets the lock be picked or not. |
SetDifficulty(target, difficulty) | bool | Sets the pick difficulty, from 0 to 2. Anything else throws ArgumentException. |
SetKeypadOpen(target, open) | bool | Opens or closes a keypad. |
LockKeypad(target) | bool | Closes an open keypad again. |
TriggerKeypad(target, success) | bool | Makes the keypad count as solved or failed. |
KeypadCode(id) | int[] | A copy of the keypad’s code. It is null when there is no such keypad. |
SetKeypadCode(id, code) | bool | Changes the keypad’s code. |
StartLockpick(target, difficulty, done) | bool | Starts the minigame. |
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | The same value as Interactions.IsAvailable. |
| Event | Gives you | When it fires |
|---|---|---|
Locked | LockArgs | A lock has been locked. |
Unlocked | LockArgs | A lock has been unlocked. |
LockpickSucceeded | LockArgs | A lockpick attempt opened the lock. |
LockpickFailed | LockArgs | A lockpick attempt failed. |
LockArgs the object a lock event is about
Locked, Unlocked, LockpickSucceeded and LockpickFailed give you this. Target is the object the event is about, and Name and Guid identify it.
public sealed class LockArgs
{
public GameObject Target { get; }
public string Name { get; }
public string Guid { get; }
}
| Property | Type | What it means |
|---|---|---|
Target | GameObject | The object that was locked, unlocked or picked. |
Name | string | Its name in the scene. |
Guid | string | Its save id, or null. |
Things to know
IsLockedworks for locks and for keypads. A keypad counts as locked while it has not been opened. (For a keypad it istruewhile itsopenflag isfalse. For anything else it isLock.Locked.)LockandUnlockreturntruewhen the object is in the state you asked for afterwards. (ADooruses its ownDoor.Lock()andDoor.Unlock(). Any other lock usesLock._Lock()andLock._Unlock(). A keypad is locked withKeypad.Lock().)PlayerHasKeychecks only for the key item. It does not check whether the player has a lockpick. (It is the game’sLock.PlayerHasKey().)SetLockpickableandSetDifficultychange what the game reads for lockpicking. They returntruewhen the lock now holds the value. A keypad has no lock of this kind, so they returnfalsefor it. Neither one raises a lock or lockpick event. (They setLock.LockpickableandLock.Difficulty. The two fields are saved with the game’s own save, next toLocked. A door saves its lock with its own save, and other lock holders depend on their own save.)Difficultyis a number from 0 to 2, and the game’s normal value is 2. It returns-1when the object has no lock, or when reading it fails.0is a real difficulty (the easiest), so a negative number is the only sign of “no lock”. CheckIsLockpickablefirst when that matters.LockedandUnlockedfire when a lock really changes state. A lock that was already in that state does not fire. If one change causes another, such as a door that is unlocked from inside a lock’s unlock step, you get one event, not two.LockArgs.Targetis the door itself for doors. For other locks it is the object that last used the lock, and it isnullif the game changed the lock before anything used it. (They are raised afterLock._Lock,Lock._Unlock,Door.Lock,Door.UnlockandDoor.ToggleLock. For other locks the target is the object that last calledLock.Use. The game also writesLockeddirectly in a few places, and those changes raise no event.)LockpickSucceededandLockpickFailedfire when a pick attempt ends. That is true for attempts your mod starts withStartLockpickand for the game’s own lockpicking. If the player cancels or looks away, neither event fires. For an attempt your mod started,doneis called withfalsein that case. (The game’sLock.LockpickSuccessCallbackis watched for the game’s own picks, andPlayerLockpicking.StopLockpickingcallsLock.AbortLockpickingon a cancel. A level change during the minigame leaves the framework’s record of that attempt armed, soStartLockpickrefuses until the game’s next stop on that lock arrives, and that stop can report one latedone(false)for the old attempt.)StartLockpickopens the lockpicking minigame on a locked, pickable object. It returnstruewhen the minigame started. AnulldonethrowsArgumentNullException. It does not check that the player has a lockpick. When the player wins, the lock is unlocked anddone(true)runs. Otherwisedone(false)runs. (It callsPlayerLockpicking.ToggleLockpickingonPlayerLockpicking.instancewith its own callback, notLock.LockpickSuccessCallback, which needs the game’s private_parentand_CallbackUnlock. On a success the callback callsUnlockand then computesunlockedas “the pick succeeded and the target is unlocked”.unlockeddrives both the lockpick events anddone. Each step is guarded, so an error in one does not stop the others.)SetKeypadOpenopens or closes a keypad. It returnstruewhen the keypad is in the state you asked for. NoLockedorUnlockedevent fires for a keypad. (It callsKeypad.SetOpen. The framework’s lock change events do not coverKeypad.)LockKeypadcloses a keypad that was open. It returns whether the keypad is locked afterwards, and fires no event. A keypad that is already closed stays as it is. (It callsKeypad.Lock(), which acts only while the keypad is open.)TriggerKeypadmakes the keypad react as if the code was right (true) or wrong (false). On success it plays its sound, runs the keypad’s success actions, and opens it when the keypad is set to stay open. It returnstruewhen the call was made and fires no event. (It callsKeypad.TriggerSuccess()orTriggerFailure(). A keypad stays open on success when it hasenableLockOnSuccess.)KeypadCode(id)gives a copy of the keypad’s code, ornullwhen the scene has no such keypad. The id is the keypad’s save id, which is theGuidfromInteractionArgs. A keypad with a row of digit buttons uses the numbers set on those buttons in the scene. A rotary lock (a dial) uses positions 1 to 32. (It uses the same lookup as other scene ids, so the keypad has to be active in the loaded scene. The digit values are not checked.)
Limits
LockandUnlockreturnfalsewhile the game is loading or saving a save. A keypad can be locked but not unlocked this way, because the game opens a keypad only when the right code is entered, soUnlockon a keypad returnsfalse. UseSetKeypadOpenfor that.SetKeypadOpenreturnsfalsefor an object that is not a keypad, and while the game is loading or saving.SetDifficultythrowsArgumentExceptionfor a number below 0, above 2 or not a number. Pass a value from 0 to 2.SetLockpickableandSetDifficultyreturnfalsewhile the game is loading or saving, or when the object has no lock.StartLockpickreturnsfalsewhen the object has no lock, is not locked, can’t be picked, when a minigame is already open, or while the game is loading or saving.- A pick that your mod starts is plainer than the game’s own. It does not play the unlock sound, does not wait for the unlock delay, and does not start the lock’s timer for locking itself again. (The unlock is a direct
Lock._Unlock(). The game’s own path plays the sound and waitsUnlockDelayinPlayerLockpicking, and re-arms the timer inLock.DelayUnlock(LockAfterTime).) SetKeypadCode(id, code)changes a keypad’s code and returnstrue. It returnsfalsefor anullor empty code, a code longer than 4 numbers, a number below 0 or above 32, while the game is loading or saving, or when the scene has no such keypad. Both kinds of keypad take at most 4 numbers. A shorter code matches on its start: only its first few digits are checked, so{1, 2}accepts any entry that starts with 1 then 2. (KeypadUIcompares the entered digits tocodein order, and the rotary lock feeds the same method (RotaryLockUI). A code longer than 4 would read past the entered digits, so it is refused.)- A code you set is lost when a save loads. The game does not save the code, and loading a save builds the keypad again from the scene, so the scene’s own code comes back. Set your code again after each load. (
Keypad.codeis[fsIgnore], so the save data does not hold it. Every save load reloads the level scene.)