About this mod
This proof-of-concept mod (which does not use the Script Extender :P and so could run on consoles) adds programmable computers (virtual keyboard, display, filesystem, disks, and interpreter unit) and a "programming gun" to use with it that shoots people with your code to the game, because somebody had to do it, dammit.
- Requirements
- Permissions and credits
- Mirrors
This proof-of-concept mod (which does not use the Script Extender :P and so could run on consoles) adds programmable computers (virtual keyboard, display, filesystem, disks, and interpreter unit) and a "programming gun" to use with it that shoots people with your code to the game, because somebody had to do it, dammit.
I really wanted to call this final mod "Type-In of The Dead: The Problem of Eval" to go along with my usual cult-classic, B-movie style of naming weird weapon mods (see the EPA Weird Weapons Season Pass page for a listing), but, man, I was kind of worried that nobody would ever find it when title searching for "computer", or "programming", or "papyrus", or "terminal". So I guess I sold out in the end, and that's just this mod's spiritual name, but this one works too because it evokes the anima of the Creation Club, and just like that, you'll be self-servicing your own fun from here on out. This is the weird weapon to end all weird weapons.
Do you wish there were more modern hidden gems like SHENZHEN I/O, Quadrilateral Cowboy, Hack 'n' Slash, and especially Else Heart.Break()? Were you disappointed at the "cancellation" of 0x10c? Do you maybe even stoop to getting your filthy fix in facets of games that don't quite involve traditional procedural scheduling, like automation in X3:TC or the dev console in Jedi Outcast? Did you somehow not wince completely at edutainment games like the Dr. Brain series or even real dreck like Bumptz Science Carnival as a kid? If so, you might be that rare "actually-likes-being-a-programmer" type. Maybe a friend once not-so-indirectly accused you of spending years "in the basement" honing your shameful craft or something, or a different one suggested that you'd be "somebody who would like puzzle games".
Well, in the words of a different "Papyrus Primer" put out by a Boston literary club with a regionally-famous Ladies Night, "sonny, a literary member fetches only ten dollars while a non-literary member fetches twenty-five dollars, unless the man who proposes your name is up to snuff". So, this one's for you, men and women of computer literacy.
You find the Gun.Runner() at Diamond City Radio (aka the Travis trailer). You shoot the gun while crouched to spawn a LecTerm (a makeshift terminal with in-world keyboard and display array made of paper card stationery), and while not crouched at NPCs to run your currently active program on them.
The LecTerm has buttons to:
* Set the displayed program as the currently active program
* Save the currently displayed program (to a holotape with a blank current line on the display, or to a shared network filesystem after typing a filename on the current line)
* Load a program (from the nearest holotape or using a filename)
* Execute the program in-place without assigning it to the gun
* Toggle the suspension of the execution of all programs globally (useful if you wrote an infinite loop using MARK/RECALL!)
* Clear the display
* Purge the shared filesystem
* Dismantle the whole terminal.
The mod implements a very spartan BASIC-like programming language I call R.E.E.D. (for Really Elementary Evaluation Dialect). The language does not currently have array support, subroutines, or any number of other convenient, modern, mental-health-conscious programming language concepts. [Papyrus itself is a very powerful language, but uncomfortably and jarringly lacks real string or array support.] Here's a program that when shot at a ordinary NPC, strips them of equipment, pops their head off, tips you a cap (in a monetary sense), and warps Preston to them:
STR NUDE UNEQUIPALL
CALL R TARGET NUDE
STR HED HEAD1
STR DIS DISMEMBER
CALL R TARGET DIS HED 1B 1B 1B
STR GPC GIVEPLAYERCAPS
CALLGLOB R GAME GPC 1D
FORM GRAVY 1A4D7
WARPTO TARGET GRAVY
(ALL PROGRAMS ARE IN UPPERCASE!! (case-insensitivity). Each instance of a program has its own workspace of variables. Special variables are prepopulated. The TARGET holds the reference to the shot NPC. PLAYER refers to you. 0F, 1F, 0D, 1D, 0B, 1B are each respectively the 0 and 1 elements of floating point, integer, and boolean variables for convenience. 0S should be the empty Papyrus string, useful in only exceptionally limited cases.)
This is a more useless program that accumulates powers of two to add to negative pi until a threshold value is reached. There's also another element to it you might notice. I would try implementing the iterative Babylonian method for finding square roots after understanding this as an exercise.
FLOAT NEGPI N3.14159
ADD TWO 1D 1D
FLOAT ALOT 999999
FLOAT R1 1
FORM PIE 1A6396
MARK CLIFFRACER
MULT R1 TWO R1
ADD R2 R1 NEGPI
DISP R2
GREATER TEST R2 ALOT
TRUEDO TEST
RETURN
SPAWN PR TARGET PIE
RECALL CLIFFRACER
(Each instance of a program has its own workspace of jump labels, e.g. CLIFFRACER. Floating point variables are specified in the FLOAT instruction with an N instead of a - because there is no -.)
Programs using other instructions:
GOTPLUGIN GP FALLOUT4.ESM
EQUAL T 1D 1D
XOR X GP T
OR O GP T
AND A GP T
NOT N T
DISP X
FORMFROMFILE F4 249216 FALLOUT4.ESM
CAST F4 V111COMPANIONFAILSAFEWARPSCRIPT
GETPROP PR F4 V111FAILSAFEWARPMARKER
WARPTO PR PLAYER
As you may observe, the "action" revolves around specifying storing the output of simple operations into the variable specified in the first argument. Any real excitement results from being able to dynamically play with the native Papyrus functions in-game, using the CALL or CALLGLOB instructions.
To get inspiration for the native Papyrus functions, use the CK Wiki documentation:
https://www.creationkit.com/fallout4/index.php?title=Category:Papyrus
https://www.creationkit.com/fallout4/index.php?title=Category:Script_Objects
Here are the ops that should be implemented in version 0x0 (might very likely need some testing or revision since it's the initial release):
(here var prefixes a variable/register that you or the terminal needs to bind, lit means a literal for which you type the string, number, or hexid directly)
CALL varResult varCaller varFunctionName [trailingVars...]
- Uses ScriptObject.CallFunction to call a Papyrus function with name varFunctionName on the calling script object (ref or form) varCaller with mandatory trailing arguments added onto the end, storing the result of the function, if any, into varResult.
CALLNOW varResult varCaller varFunctionName [trailingVars...]
- Same as CALL, but does not wait for, or deliver, a result.
CALLGLOB varResult litOwningClass varFunctionName [trailingVars...]
- Uses Utility.CallGlobalFunction to call a Papyrus function with name varFunctionName from the class varOwningClass (e.g. Utility, Game, Math) with mandatory trailing arguments added onto the end, storing the result of the function, if any, into varResult.
CALLGLOB varResult litOwningClass varFunctionName [trailingVars...]
- Same as CALLGLOB, but does not wait for, or deliver, a result.
CAST varResult lit
- Uses ScriptObject.CastAs to cast one object explicitly into another type. The recast object is stored back into the the varResult variable. Often, because of the way casting works in the game, this will not undo inheritance, so watch out for that.
MARK litLabelName
- LBL in other languages which place less of a priority on referencing Morrowind, this names a point in the program you can jump back to with the RECALL instruction.
RECALL litLabelName
- JMP in less cultured languages, this retreats to a point (moves the program counter) recorded with a named label by MARK.
RETURN
- Since there are no subroutines in this version of the language, this ends the program by moving the program counter to the end. Maybe I should have called it ALMSIVI.
TRUEDO varTruthValue
- Very primitive conditional. If the argument refers to a boolean value with the value True, then execute the next line. Otherwise the program counter jumps ahead a line to skip the forthcoming line.
SPAWN varResultRef varAnchorRef varFormToPlace
- Spawns an entity (item, NPC, etc.) of class/form varFormToPlace at the varAnchoringObject, storing a reference to the newly created entity/ref in varResultRef.
WARPTO varAnchorRef varWarpingRef
- Moves a entity to another.
ADD varResult varOperandA varOperandB
- Adds varOperandA (floats) to varOperandB, storing the result in varResult.
MULT varResult varOperandA varOperandB
- Multiplies varOperandA with varOperandB, storing the result in varResult.
DIV varResult varOperandA varOperandB
- Divides varOperandA by varOperandB, storing the result in varResult.
SUB varResult varOperandA varOperandB
- Substracts from varOperandA varOperandB, storing the result in varResult.
MOD varResult varOperandA varOperandB
- Finds the remainder of varOperandA (ints) divided by varOperandB, storing the result in varResult.
ISNULL varResult varOperandA
- Stores whether varOperandA is None in varResult. (intended for use with Forms.)
NOT varResult varOperandA
- Stores the negation of varOperandA (bools) in varResult.
AND varResult varOperandA varOperandB
- Stores whether varOperandA and varOperandB are both True in varResult.
OR varResult varOperandA varOperandB
- Stores whether varOperandA OR (inclusive, either )varOperandB is True in varResult.
XOR varResult varOperandA varOperandB
- Stores whether varOperandA OR (exclusive, one or the other) varOperandB is True in varResult.
GREATER varResult varOperandA varOperandB
- Stores whether varOperandA is greater than varOperandB in varResult.
LESSER varResult varOperandA varOperandB
- Stores whether varOperandA is lesser than varOperandB in varResult.
EQUAL varResult varOperandA varOperandB
- Stores whether varOperandA is equal to varOperandB in varResult. (supposed to work for multiple primitive types.)
SLEEP varSleepSeconds
- Waits a number of seconds (Utility.Wait) before continuing. Practically useless for conventional timing because R.E.E.D. instructions are VEEERRRY slow.
STR varResult litStringToStore
- Stores an internal R.E.E.D string litStringToStore into varResult. Note that this is NOT a native Papyrus string, but is used for programming.
FORM varResult litHexadecimalFormID
- Stores a specific form pointed to by a literal hexadecimal Form ID into varResult. You can find these form IDs by using the dev console or creation kit on PC, or by consulting wiki listings (don't abuse the knowledge that Preston is 1A4D7).
FLOAT varResult litFloatingPoint
- Stores a floating point number into varResult. If the number is negative, enter an N in front of it, then the whole part of the number, then a ., then the fractional part of the number. Negative pi is about equal to N3.14159.
GETBASE varResultForm varQueriedRef
- Assuming varQueriedRef is an ObjectReference (an instance of a class of object), attempts to pass back its Form (the prototype used to spawn things). Your Preston (1A4D7) is different from the template for Preston (19fd9).
GETPROP varResult varObjectQueried litFieldNameQueried
- Attempts to fetch the value stored in the property litFieldNameQueried of the object varObjectQueried, storing it in varResult.
SETPROP varModifiedObject varModifyingValue litFieldNameChanged
- Attempts to set the value stored in the property litFieldNameChanged of the object varModifiedObject to the value stored in the variable varModifyingValue.
GOTPLUGIN varResult litESPorESMFullName
- Looks to see if a plugin (e.g. ""DLCCoast.ESM") is loaded. If so, return True to varResult, otherwise False.
FORMFROMFILE varResult litHexFormID litESPorESMFullName
- Tries to grab a form from a named plugin or master litESPorESMFullName with known internal form ID litHexFormID, storing it in varResult.
DISP varVariableToPrint
- Attempts to print the variable's value as a Float if the variable is Numeric (Int, Float, Bool). If the variable is a form, prints the Float decimal representation of its internal ID. On systems with debugOnly capability (PC, not XBOX), ALSO prints the native Papyrus string representation suggested by the object's type.
NOP
- Does nothing. "Yas."
=== PAQ's - Preemptively Answered Questions ===
Q: Why does this require Wasteland Workshop?
A: It had some nice letter art, and I didn't want to get in a graphics editor and make a new symbol alphabet.
Q: Should I use this on my serious Dead-Is-Dead, hardcore survival savegame?
A: I'm guessing you should not. This mod is far from the conceptual stage, but it is still pretty "experimental". It will probably need some updating to live up to the current level of promise.
Q: Who could ever be the target audience for this?
A: People with a lot of patience, who badly want to get at the game internals but can't or won't get their hands on the Creation Kit. On consoles, this kind of thing would be very helpful, and probably a few degrees of control above what you get with a holotape cheat mod. (If you just want to spawn and warp to arbitrary things, check out Form F From Address Space!)
Q: The instructions are REALLY slowly executed. Can't do anything fun since it's so far away from realtime. Anything you can do about that?
A: An interpreter is being run basically inside another interpreter in a very complex game, so you can't expect very much. Moreover, it's like basically against the parent one's will since there is no fast string or array support and you have to do silly, wasteful things to compensate. Could I make more efficient data structure workarounds for the associative arrays and strings I rolled on my own? Yeah, the maps could be hashmaps or tries, or something, but I'm not seeing the order of magnitude improvements I would like happening in exchange for the work.
Q: The programs are unpleasant to enter, can I have a better and more responsive keyboard layout, multiline editing, VIM style hotkeys, etc.? Backspacing so much when I make an error makes me so upset.
A: This is a pretty plausible suggestion, but the audience for this mod is so incredibly small that it seems like this would be a compoundingly pointless waste of time to make a better text editor here. It's still better than ED(LIN), or flicking bitswitches on historic computers, or singing to a microphone in Morse Code. Also, I was inspired by the ancient and departed phenomenon of hexadecimal array type-in programs you would find in magazines, and you didn't want to mess those up without some checksum procedure backing you up. That's an old-time recipe for a wasted afternoon. As it is, I write the programs out on paper and check each line.
Q: I would like an instruction added (or, you messed up a bunch of instructions, you dunce)! Can you add / fix it?
A: This I probably would do, given enough detail about the problem (for example, a program to try to run). I don't have a lot of future plans for this mod that are very extensive. Since I don't want to use the Script Extender, I don't really want to attempt integrating this into the Settlement/Power system.
Q: Can you open source this pointless thing?
A: I'd like to clean the code up a bit first, but if there's enough interest, maybe I'll put that out there for somebody to extend.
Q: Why didn't you use the script extender or Scaleform/Flash to do this? You could have made some odd custom DLL solution and hooked this up to a fully-formed working interpreter system, like PHP.
A: I wanted it to be able to work entirely in the game (on consoles, etc.).
Q: Why BASIC or Assembly and not C, Pascal, or ALGOL 60? Second thought, I read a Springer book on Computational Geometry and Evolutionary Computing in Erlang and somebody's blog on Haskell, Chef, and Piet, why not those languages? It's 2018, why not Vala or Rust? The game's set at MIT, why not LISP?
A: It was a lot easier to implement as a mod starting from capabilities of dynamic typing, fixed arrays of integers, and some letter models in the world. That fancy parsing power takes a lot of sophistication. Think about how short a compilers book on this kind of language would be. That recursive evaluation, function chaining, and infix notation is expensive, dude.
Q: Why did the developers seemingly rip out plans for a Python interpreter?
A: I'm guessing Sony would have really not liked that prospect. Would have been really cool to have fast terminal programming in the game. Would also have been nice to have real arbitrary-length array and string handling in Papyrus! I think it would be super neat if Starfield had interactive, in-game computer systems. That would be the kind of great leap forward in interactivity in an "immersive sim"/"systemic game" that we expect. I could be wrong, but I'm not expecting to see this kind of gameplay in Fallout 76.
Q: Why did you not add array or string support to your language if you miss it so bad in Papyrus?
A: Because I am but a single hypocrite with limited time to spend in this instance. Bethesda Engine Workaround Fun Fact: Each array list or string used by the computers in the mod corresponds to a spool of string or a bread box in an unloaded cell somewhere.
Q: Is this compatible with Fallout 4 VR? Can I play as a programmer, BUT IN VR???
A: I haven't put on my headset to check. It should be, but there are strange things missing from that build branch.
Q: What's the point of sitting in front of a computer playing "sit in front of the computer"?
A: Power over remote and esoteric things in an otherwise alive world makes the world feel more alive.
Q: You've wasted a somewhat large part of your life on a lot of really strange, needlessly complex "weapons" for Fallout 4 (see the EPA Weird Weapons Season Pass) that don't even look good or are particularly lore-friendly. Are you insane / stupid / on drugs / a space alien?
A: That many people can't be wrong, can they?