This chapter is unfinished and will be used to write up info on further customizing of "PersonGlobals.iff" as I discover it; at the moment, I'm working on guest processing and the possibility of plugins. The anti-nude code only requires straightforward disabling, so I'll start with that.
Disabling the nudity check
In SS and MM, there is some anti-nudity coding which will change a nude Sim to a fully dressed one whenever that Sim is idling. This only applies to adults and, as well as taking the fun out of any "naughty" objects, is incredibly frustrating when trying to test a custom nude skin in the game. Sims are "processed" in "PersonGlobals.iff", so I look at the "main" method which leads me to "person main loop" (#8283) which leads to "do idle and mood" (#8303) which only contains a call to "do idle core" (#8229), and "do new adult idle" (#8330), which contains its own idling code. I'm looking for a combination of adult and idle, so this may well be it. And at the bottom of #8330 I do indeed find: "39: my Person Data current outfit equals 1? Then 40: Change Suit/Accessory (id=0) from person suits." That's the culprit. The solution is to change both true and false outcome of line 39 to 1.
Military school: to go or not to go
It is a sign of the general modern Western hostility to minors that adult Sims may screw up any way they like, but nine-year-olds are sent off to a prison masquerading as school, not for theft, murder or torture, but for low marks. I hope those children return in tanks and bomber planes and reduce the neighbourhood to ground zero. That won't happen, though, because by the time the Sim-child dons its little uniform and trots off, its identity has already been erased. This I intend to prevent, or, even better, make subject to the player's judgement, since there are children one may want to get rid of.
The plan: a dialog inserted just after the low-marks check, with a Yes/No button. Yes means Off-you-go and No resets the child's marks to D+, so as not to get the same question twice in a day. However, PersonGlobals, like all globals, doesn't have its own dialog strings, and even if I added some, would not access them, but would show a dialog with "error - missing string". I have to use the strings in another object. The obvious choice for this other object is the IFF referring to the global file in its GLOB block, but in the case of PersonGlobals this would be a Sim IFF. I would have to alter all Sim IFFs and then pray the game won't remove my changes. It's better to use a static object. Like "PedPortal.iff", which is invoked by PersonGlobals anyway for the lot-leaving code.
First, an analysis of the military school check in BHAV8312 of PersonGlobals. This tree is only entered if the "my person" Sim is a child, and its very first line checks if school marks are higher than 13 (remember, 0 is A+, 3 is B+ and so on; "F" covers 12 to 15). If so, checks follow to see if the Sim is ready to leave (ie. not sitting or sleeping), then the tree jumps from line 23 to line 7: school money is subtracted, then to line 1 and 2: the Simchild is deleted from the family, then a lot of jumping between lines as the child dresses in uniform and a possible snapshot is taken with the caption "[name] is off to military school!" and the routine ends in line 4 "(glob) Set to leave" which calls "PedPortal.iff" containing the child's sobbing goodbye. The child, by that time, no longer exists as a Sim; any dialog should be inserted before line 7. A quick way to disable military school altogether is to make both outcomes of line 0 True. This may slow the game, as the child's grade continues to be F and PersonGlobals is therefore directed to BHAV8312 every second, only to be blown off. (I tested this, and it doesn't slow the game on computers with a 1Ghz or faster processor.)
So, what would be the proper course of action: have line 0 jump to a question whether the child really should go to military school. Only, the question can't be inserted into the BHAV directly, so this BHAV must run a BHAV in another object, already decided to be the PedPortal, wait for that other BHAV to exit, and use the outcome to either set the grade to D+ or continue the military school processing. Better still, in case the PedPortal produces an error, first raise the grade to D+, then ask the question, then, if the answer is Yes, lower the grade again and continue.
There are two ways to run an interaction in one object remotely from another. (There used to be three, but the third one is depreciated.) The first is by calling a "named tree", a BHAV whose name string corresponds to a string in STR#303 of "Global.iff", although it should be possible to use STR#303 in the calling object. The second is by "pushing" an interaction, that is, adding a task to a Sim's queue. The problem with the second method is that it may take a while for the task, in this case the showing of a dialog, to be started. The military school BHAV would add the task and then continue its child-eliminating code, the dialog appearing far too late.
So, what code is added to PersonGlobals' BHAV8312:
Line 0, whose outcomes were line 8 and True, has had line 8 changed
to line 26. Next, lines 26 to 30 are added:
Line 26: 2: 57, 9, 1280, 1810; 27, Error
Line 27: 2: 0, 0, 1280, 1802; 28, Error
Line 28: 31: -16647, -32282, 4, 0; 29, True
Line 29: 28: 306, 0, 513, 0; 30, True
Line 30: 2: 57, 13, 3584, 1810; 8, True
Line 26 sets the grade to D+, a safe grade that won't drop back
to F within one Simday. Line 27 sets the stack object ID to 0 (there
is no value stored in this ID yet, or I'd store the existing value in
a local variable first). Line 28 finds the first pedestrian portal.
IFF Pencil doesn't like negative numbers being entered, so it's best
to enter the hexadecimal parameters:
31: F9 BE E6 81 04 00 00 00
Notice anything about these parameters? Yes, the first four bytes are, in reversed order, the GUID of PedPortal.iff. Should no pedestrian portal be found (deleted by a careless user?) the routine quits with a True, ie. it doesn't needlessly raise an error. Likewise in the next line, which tries to run a routine in the pedestrian portal; if the routine isn't found, because the game uses a regular portal instead of the hacked one, the game also shrugs and smoothly continues. If it has been found, line 30 assumes that it has either lowered the grade, in which case hop back to line 8 and send the child off; else, again, return True.
Looking at line 29: this takes a name from a STR# resource, tries to find a routine of that name in the object whose GUID is currently the stack object ID, runs it if possible (True if found, Error if not) and then continues its own routine. This is convenient for when one object must run the same routine in different objects, for instance, when testing every object in the room for the "Am I a mirror" routine, which may have a different BHAV number in each object. Unlike dialogs, which can only use strings from STR#301, named tree strings can have their STR# number specified. The usual named tree resource is STR#303, but to be on the safe side, I'm using STR#306, which I haven't seen used anywhere else, so that this resource won't clash with possible STR#303 blocks in NPCs that refer to PersonGlobals in their GLOB blocks. Hexadecimal parameter 5 determines the string number, while parameter 6 can have values 0 to 2: run in my stack, run in stack object's stack, push onto my stack. Hex parameter 3 can be odd or even, the first meaning "Global" and the second "Private". If "Private", STR#306 will be looked up in PersonGlobals, else, it will be looked up in Global.iff, where it won't be found. At the moment it won't be found in PersonGlobals either, so I'll add it:
And in this new block I'll add the following strings, one for the military school hack and two for a future project:
Note that "Use different languages" is unchecked here, else, the object would work in a US English installation but not in a Spanish or Japanese installation. In blocks of type STR#303, this option can't even be checked.
Now it's time to see to the pedestrian portal. I promised PersonGlobals a routine called "display military school dialog". Now it so happens that this dialog already exists: it's BHAV4115 and it goes like this:
Normally this routine is called via a BHAV number from PersonGlobals. Not knowing why it's called, it does a safety check: is the caller a child, and one with sufficiently low marks? I can abuse the second check to pop in some coding of my own to show that other military school dialog I had in mind:
The old code jumps around a bit: first two checks, then at the bottom
something is stored in the stack object ID, then one line upwards a dialog
(the sobbing goodbye) is shown and that ends the routine. In the hacked
version, the low-marks check will branch into a "goodbye" if the marks
really are at F level, and into a "leave Y/N" if not; in this way, I
can use an existing BHAV for both purposes. The first line of the second
branch also fills the stack object ID, but since, due to the way it's
called, the portal itself owns the thread now, "my object ID" would
result in a portal icon being shown, so I must use "my person data
neighbour id" since a "neighbour" is a living Sim. That's why this
second dialog uses a "neighbour" icon. A dialog is shown with Yes/No
buttons, where Yes sets the grade back to F before returning to
PersonGlobals, with instant results. The code for the added lines is:
Line 4: 2: 0, 31, 1280, 4618; 5, Error
Line 5: 36: 0, 26, 256, 1024; 6, True
Line 6: 2: 57, 13, 1280, 1810; True, Error
How to create dialogs is covered in All kinds of dialogs.
Am I done yet? No, because I told the dialog to use the 26th string in STR#301, and STR#301 has no such string. So I add a 26th entry and copy and paste all language strings "$Me is off to military school!" from PersonGlobals' snapshot texts in STR#305, to ensure that the new dialog is multilingual. Hacking such basic parts of the game is no use if the hacks only work for English users! The end result: