Cheat Mod Ren'Py The Null Hypothesis Cheat Injector [v2.8] [Sleepingkirby]

5.00 star(s) 1 Vote

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
Thank you for looking into this and for the explanation. It makes sense, I understand the code now.

1. I do think it would be a cool little feature. Nothing major, but it's helpful to those who don't want to grind through so many study and combat sessions. Instead we could simply click the button once to set the score to e.g. 500 points. That would save a lot of time and grinding. Still, I do understand it might not be a major help if people don't care about the leader board.

So whether or not "it's worth the effort" depends mostly on the degree of coding effort.

One simplistic solution could be to have a button that sets the score to e.g. 500. So something crude like this:

Code:
@property
        def leaderboard_score(self) -> Dict[str, float]:
            score = {"combat": 500, "cognitive": 500}  
            return score
I looked into that. Those numbers are aggregates that are overwritten by the leaderboard_score() function I mentioned in my previous post every time you do something to update the score. I tried to manually change those and it made no difference. Didn't even update the leaderboard.

2. There's space for a button in the canvas/frame used for the "Name" table header. I don't know how UIs work with Renpy, so I don't know how easy it is to add a button there. This might be a rough idea:

View attachment 5168625
Those might work out. The thing to remember is, there's no way to click on something to set the points to 100. As the scores are procedurally calculated every time. So the best you'd get is a button that, when you click, runs the function to add another study or training event to your history. It would up the score, but not up it to a specific amount. It would only increase it like if you had just had another session of study or training. I'll look into this more tomorrow.
 

mrbombadil

Member
Jul 25, 2024
133
363
82
I looked into that. Those numbers are aggregates that are overwritten by the leaderboard_score() function I mentioned in my previous post every time you do something to update the score. I tried to manually change those and it made no difference. Didn't even update the leaderboard.


Those might work out. The thing to remember is, there's no way to click on something to set the points to 100. As the scores are procedurally calculated every time. So the best you'd get is a button that, when you click, runs the function to add another study or training event to your history. It would up the score, but not up it to a specific amount. It would only increase it like if you had just had another session of study or training. I'll look into this more tomorrow.
Oh I see what you mean now. But at least when you click this new button you could have it automatically add e.g. 20 entries of study / combat instead of just 1.

The button text could be e.g. "Add 20 study" when on the study leader board and "Add 20 trainings" when on the combat.
 

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
Sure. Got a file not found error, didn't notice this while patching it the first time, though.
Code:
 Searching for RPA packages
   + Unpacking "archive.rpa" - 452466189 bytes
Could not extract file  from archive: [Errno 2] the requested file . does not exist in the given Ren'Py archive

  Cleaning up temporary files...

  Creating cheat...
./scripts/interfaces/main_menu.rpy patched
./scripts/interfaces/base.rpy patched
./scripts/base/config.rpy patched
./scripts/mechanics/utilities.rpy patched
./scripts/base/player.rpy patched
./scripts/interfaces/Player_menu.rpy patched
./scripts/interfaces/sex.rpy patched
./scripts/mechanics/approval.rpy patched
./scripts/sex/request.rpy patched
./scripts/interfaces/interactions.rpy patched
./scripts/mechanics/movement.rpy patched
    Success! Cheats are now enabled!

  Cleaning up temporary files...

----------------------------------------------------

   Finished!
Started a new game, played until day4, clicked relationship status and got the same error i posted before.
edit: Will make Rogue my girlfriend and cheat on her, we'll see what happens then, will update this post then.

Edit: Done.
View attachment 5168700 View attachment 5168699
Same error as before.
You don't have permission to view the spoiler content. Log in or register now.
Maybe its on my end and renpy engine is messing with the appdata folder and saves?
The file not found error is benign. It's from rpatool saying it can't extract the . directory into the . directory. Which, yeah, that's correct.

Pertaining to the permanent tracker, I've "found" "the issue". Check this out. This is a search for the words "History.permanent" in all the .rpy files:
Code:
sleepingkirby@AnimalKirby:~/Games/TheNullHypothesis-0.8b1-pc/game$ findExecGrep -Hwin "*.rpy" "History.permanent"
./scripts/mechanics/utilities.rpy:10:        if C.History.permanent.get("cheated_on_flirting_in_public"):
./scripts/mechanics/utilities.rpy:11:            del C.History.permanent["cheated_on_flirting_in_public"]
./scripts/mechanics/utilities.rpy:12:        if C.History.permanent.get("cheated_on_date"):
./scripts/mechanics/utilities.rpy:13:            del C.History.permanent["cheated_on_date"]
./scripts/mechanics/utilities.rpy:14:        if C.History.permanent.get("cheated_on_relationship"):
./scripts/mechanics/utilities.rpy:15:            del C.History.permanent["cheated_on_relationship"]
./scripts/mechanics/utilities.rpy:22:                if Player.History.permanent.get(f"cheated_on_{C.tag}_with_{other_C.tag}_flirting_in_public"):
./scripts/mechanics/utilities.rpy:23:                   del Player.History.permanent[f"cheated_on_{C.tag}_with_{other_C.tag}_flirting_in_public"]
./scripts/mechanics/utilities.rpy:24:                if Player.History.permanent.get(f"cheated_on_{C.tag}_with_{other_C.tag}_date"):
./scripts/mechanics/utilities.rpy:25:                   del Player.History.permanent[f"cheated_on_{C.tag}_with_{other_C.tag}_date"]
./scripts/mechanics/utilities.rpy:26:                if Player.History.permanent.get(f"cheated_on_{C.tag}_with_{other_C.tag}_relationship"):
./scripts/mechanics/utilities.rpy:27:                   del Player.History.permanent[f"cheated_on_{C.tag}_with_{other_C.tag}_relationship"]
All that? That's that custom function I wrote to remove cheating on a character. That's the ONLY place it exists.

Now, when I search for just "permanent", this is one of the few lines that show up:
Code:
./scripts/base/start.rpy:463:                for tracker in ("immediate", "event", "recent", "last", "yesterday", "daily", "weekly", "season", "chapter", "persistent", "permanent"):
And if you go to that line, you'll see right above it:

Code:
    ## can delete this eventually
    python:
        for C in GameState.all_Characters:
            if not hasattr(C.History, "trackers"):
                C.History.trackers = {}

                for tracker in ("immediate", "event", "recent", "last", "yesterday", "daily", "weekly", "season", "chapter", "persistent", "permanent"):
The comment of this section eventually being removed plus no references at all to History.permanent, most likely means RonChon is gradually phasing out the permanent history tracker. Which is why it works on mine but not on a new game (which is probably one of the reasons why, at the next version, old saves won't be compatible.). Even if it's defined in the tuple, it probably isn't being initialized. That actually makes my job easier. I just need to remove all the lines with permanent on my custom function. But I'll do that tomorrow with testing to see how mrbombadil 's request works out.

pepplez
If you want to fix this for the time being, go into "game/scripts/mechanics/utilities.rpy and any line that has History.permanent, just remove that entire line.
 
Last edited:

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
Oh I see what you mean now. But at least when you click this new button you could have it automatically add e.g. 20 entries of study / combat instead of just 1.
We'll see if I can make that work. The way renpy works is, when a button is clicked, it runs 1 function. I think I can daisy chain the actions but doing 20 might be bad for the game engine.
 
  • Haha
Reactions: pepplez

mrbombadil

Member
Jul 25, 2024
133
363
82
So the
We'll see if I can make that work. The way renpy works is, when a button is clicked, it runs 1 function. I think I can daisy chain the actions but doing 20 might be bad for the game engine.
As far as I've understood, in Renpy, a Python function called by a button can do essentially anything regular Python can, including invoking multiple game actions. So:
Code:
screen test_screen():
    textbutton "Cheat Study/Train" action Function(cheat_train_and_study)

python:
    def cheat_train_and_study():
        # Adds 20 "studied" and 20 "trained" entries
        for _ in range(20):
            Player.History.update("studied")
            Player.History.update("trained")
 
Last edited:

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
So the


As far as I've understood, in Renpy, a Python function called by a button can do essentially anything regular Python can, including invoking multiple game actions. So:
Code:
screen test_screen():
    textbutton "Cheat Study/Train" action Function(cheat_train_and_study)

python:
    def cheat_train_and_study():
        # Adds 20 "studied" and 20 "trained" entries
        for _ in range(20):
            Player.History.update("studied")
            Player.History.update("trained")
Yeah, I know. That's how I reimplemented skill points as well as implementing that custom function to remove cheating that I was just talking to Pepplez about. But there's a lot of risk in injecting your own functions. There's risk of name conflicts, breakage, munging an existing .rpy file or class via a bad injection, polluting a class, accidentally changing the scope (especially since python is a white space sensitive language), etc. You don't inject code into an existing code base lightly. Also, a custom function is rigid, as proven by RonChon's removal of the permanent history tracker. Most of the injections I've written are not. This means that they're more easily/more likely to be broken with newer versions of the game.
The easier solution is to daisy chain the command. If I recall correctly, you can throw an array of functions into action to have it run multiple functions. But there's a concern of lag/stabiltiy. If you know how renpy works, it's similar to react in that the screen refreshes and redraws on certain variable(state for react) changes. If you're running the same function that requires a redraw 20 times,there will probably be a significant lag because it's redrawing the screen 20 times (which was what Pepplez was talking about with the post "I see lag incoming"). The stability comes with how good the actual renpy engine is. Multiple and quick async variable updates and rewrites in quick succession can case things like incomplete writes and race conditions. And if you think "Nah, that wouldn't happen" that's the reason why Zelda TOTK and BotW have dupe glitches. You overload the via speed or amount (often both) system to desync the states on multiple interfaces so the communication is off in a way that is to your advantage.State machines are fine and dandy for slower, simpler systems, but when you start moving fast enough, you can't guarantee the states will be in a sane state.
Which comes back to, I'll look at it. We'll see what can be done.
 
  • Like
Reactions: mrbombadil

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
So the


As far as I've understood, in Renpy, a Python function called by a button can do essentially anything regular Python can, including invoking multiple game actions. So:
Code:
screen test_screen():
    textbutton "Cheat Study/Train" action Function(cheat_train_and_study)

python:
    def cheat_train_and_study():
        # Adds 20 "studied" and 20 "trained" entries
        for _ in range(20):
            Player.History.update("studied")
            Player.History.update("trained")
I know you said to add like 10 points per click but... this is pretty good as is and I was running a test. I like having the option to bump up the points (and the ranks) little by little.
 

mrbombadil

Member
Jul 25, 2024
133
363
82
I know you said to add like 10 points per click but... this is pretty good as is and I was running a test. I like having the option to bump up the points (and the ranks) little by little.
Looks awesome! And yeah, with a simple click like that it's no problem to have it "just" add one session. It's fast to click 10 times anyway.

Thanks a lot :)
 

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
New version 2.3 has been updated. Thanks to pepplez for catching that the permanent history tracker is gradually being deprecated. New feature as requested by mrbombadil that allows you to increase your leadership score by adding events of "studied" or "trained" into your history.

As always, make sure to reapply to a fresh copy of the game.
 

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
That's not too surprising consider they said it breaks old saves. Things that do that usually means a pretty significant code rewrite. The last 2~3 fixes I had to do involved a and . I usually ask things like "What doesn't work?" and "What's the console output?" but given how big the change seems to be from where I'm sitting, I'm just going to wait until I see the code.
 
  • Like
Reactions: pepplez and bazbj77

markus T

Engaged Member
Jun 11, 2017
3,117
3,719
498
Is there a way to disable the stat check on the wardrobe so you can have them wear whatever you want?
 

sleepingkirby

Well-Known Member
Aug 8, 2017
1,323
1,955
262
5.00 star(s) 1 Vote