Forum Announcement, Click Here to Read More From EA_Cade.

Verify_School logic not making sense

TriplisTriplis Posts: 3,048 Member
edited March 5 in Nominated Threads
I figured maybe talking about this will reveal something I'm not seeing. Last night I revisited the mod I'd made that allows Child and Teen Sims to Quit or Rejoin School. I'd managed to use a script, so that the game no longer pushed school on you, either when loading or when aging up.

But someone pointed out to me a glaring flaw in this, that without having school pushed on you when you age up, unplayed sims would simply never join it or go there, unless you switched to them and joined manually.

This was a good reason, I felt, to try to revisit separating the two, so that it's only loading where the game no longer pushes school on you and aging up still does.

I tried some different approaches last night, searched files, studied the code, but the logic simply didn't make sense.

The code for the Verify_School method is as follows (from sim_info.py):
def verify_school(self, from_age_up):
        if from_age_up or self._time_sim_was_saved is None:
            if self.is_child:
                self.create_homework(self.CHILD_HOMEWORK)
            elif self.is_teen:
                self.create_homework(self.TEEN_HOMEWORK)
        self.update_school_career()

Then in sim.py, there's this:
if self._simulation_state == SimulationState.INITIALIZING:
                self.sim_info.verify_school(from_age_up=False)

And in aging_mixin.py, in the advance_age method, there's this:
if next_age > Age.TODDLER:
                self.verify_school(from_age_up=True)

These are the only two places, a search shows, where the verify_school method is called. And the method that verify_school calls ("update_school_career()") is only called within sim_info.py

It appears fairly intuitive to me. The call in aging_mixin checks if the sim is aging up to something past Toddler. If they are, verify_school is called. Verify_school checks if the sim in question is a child or teen and generates their little homework booklet if they are. At the end, it calls update_school_career no matter what, which calls this:
self.ensure_age_based_career(Age.CHILD, SimInfo.SCHOOL_CAREER)
        self.ensure_age_based_career(Age.TEEN, SimInfo.HIGH_SCHOOL_CAREER)

To verify that their age is Child or Teen and pass along the appropriate school career to be added to them.

The part where my understanding breaks down is this:

1) I can't seem to figure out what "self._time_sim_was_saved is None" actually means and how it relates to the code in sim.py. I'm guessing it relates to it for some reason.

2) It appears that the code in sim.py is passing along the value to verify_school "from_age_up=false" (because it's not from aging up, it's from loading) and the code in aging_mixin.py is passing along the value to verify_school "from_age_up=true" (because it is from aging up).

And yet, if I change the verify_school method to something like this:
def my_verify_school(original, self, from_age_up):
	if from_age_up or self._time_sim_was_saved is None:
		if self.is_child:
			self.create_homework(self.CHILD_HOMEWORK)
		elif self.is_teen:
			self.create_homework(self.TEEN_HOMEWORK)
		
	if from_age_up:	
		self.update_school_career()
		
	original(self, from_age_up)
It doesn't appear to do anything. The logic is supposed to be that instead of calling update_school_career() no matter what, it only gets called if the "from_age_up" value passed to verify_school is "true." Which should, in theory, mean that update_school_career() won't get called if it's the code in sim.py that is doing the check, thereby negating the "on load" part.

But it's not doing any such thing that I can tell.
Mods moved from MTS, now hosted at: https://triplis.github.io
Post edited by EA_Cade on

Comments

  • Options
    NeiaNeia Posts: 4,190 Member
    In your example, the last original(self, from_age_up) is calling the vanilla function, which itself calls the self.update_school_career() no matter the value of from_age_up.
  • Options
    TriplisTriplis Posts: 3,048 Member
    edited April 2017
    @Neia That's odd. Because in the script for no auto enroll at all, I did this:
    @Triplis_School_Push_Injector.inject_to(sims.sim_info.SimInfo, "ensure_age_based_career")
    def my_ensure_age_based_career(original, self, age, career):
        has_career = career.guid64 in self.careers
        if self.age == age:
            if not has_career:
                return
        elif has_career:
                self._career_tracker.remove_career(career.guid64, post_quit_msg=False)
    
        original(self, age, career)
    

    And it works fine. So if the "original" part at the end is calling the vanilla function, why does it not cause the same issue for this script? (Edit: Oh, I wonder if it's the return part that saves this method from disaster... return causes it to move on rather continuing on and hitting the original method?)

    I guess I've misunderstood something about the original call. The reason I had it in there at all is because I was getting nothing but errors if I left it out completely. I want to overwrite the existing script, but simply writing to it without that "original" call at some part of it doesn't seem to work. Maybe there is no way to overwrite it without errors. I dunno.

    I'm confused.
    Mods moved from MTS, now hosted at: https://triplis.github.io
  • Options
    TriplisTriplis Posts: 3,048 Member
    Hmm. Maybe it's the injector that's the problem. Now I'm wondering if it only supports adding to a function, not overwriting anything in it.
    # Python function injection
    # By scripthoge at ModTheSims
    #
    from functools import wraps
    import inspect
    
    # method calling injection
    def inject(target_function, new_function):
        @wraps(target_function)
        def _inject(*args, **kwargs):
            return new_function(target_function, *args, **kwargs)
        return _inject
    
    # decarator injection.
    def inject_to(target_object, target_function_name):
        def _inject_to(new_function):
            target_function = getattr(target_object, target_function_name)
            setattr(target_object, target_function_name, inject(target_function, new_function))
            return new_function
        return _inject_to
    
    def is_injectable(target_function, new_function):
        target_argspec = inspect.getargspec(target_function)
        new_argspec = inspect.getargspec(new_function)
        return len(target_argspec.args) == len(new_argspec.args) - 1
    
    I've never really deeply explored how it works. I probably should.
    Mods moved from MTS, now hosted at: https://triplis.github.io
  • Options
    NeiaNeia Posts: 4,190 Member
    edited April 2017
    Yes, it seems like it's the return that allowed you to skip the original part in the ensure_age_based_career. Injections aren't meant for overwriting completely functions, but to add some behavior either before or after the original one, at least that's what I understood :) Deaderpool has a post explaining how injections work here : http://modthesims.info/showthread.php?p=4751246#post4751246

    You could simply overwrite the fucntion with something like
    def my_verify_school(self, from_age_up):
           if from_age_up or self._time_sim_was_saved is None:
    		if self.is_child:
    			self.create_homework(self.CHILD_HOMEWORK)
    		elif self.is_teen:
    			self.create_homework(self.TEEN_HOMEWORK)
    		
    	if from_age_up:	
    		self.update_school_career()
    
    sims.sim_info.SimInfo.verify_school = my_verify_school
    


    or you could do something like this if you want to keep the original code in case there's some change in it :
    def my_verify_school(original, self, from_age_up):
    	if from_age_up
                   original(self, from_age_up)
    
  • Options
    TriplisTriplis Posts: 3,048 Member
    @Neia Thanks! Very clever approaches. :)

    Both function error free and the logic of them are sound, as far as I can tell, which is why it's utterly baffling to me that it still isn't preventing school from being foisted on the sim when they load.

    It's really bugging me that the logic seems sound, but it isn't working in practice as it should... darn logic.

    I mean, I even tried doing the 1st approach in your post, but with no reference to update_school_career at all, just to see what would happen and school was still foisted on the child sim I was checking. I wonder if there's a way to print the contents of the function while in-game, cause it really seems like its contents are not getting overwritten (or, in the case of the 2nd approach and others like it, its contents, including update_school_career(), are getting called as if there is no special restriction on it).
    Mods moved from MTS, now hosted at: https://triplis.github.io
  • Options
    NeiaNeia Posts: 4,190 Member
    There's probably some error in there :D

    You can print content in the cheat box with
    output = sims4.commands.CheatOutput(_connection) 
    output('something')
    

    (don't forget to import sims4.commands).

    Or you could use an external log like Scumbumbo described here : http://www.modthesims.info/showthread.php?p=4679461#post4679461
  • Options
    TriplisTriplis Posts: 3,048 Member
    @Neia Probably lol.

    https://www.youtube.com/watch?v=qLk81XnkGUM

    I'll look into logging and see if that reveals anything.
    Mods moved from MTS, now hosted at: https://triplis.github.io

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file
Return to top