Centralizing story flow, minimising use of `jump` #34

Closed
opened 4 months ago by Bowie · 16 comments
Bowie commented 4 months ago
Collaborator

After the big refactor lands (my sympathies @coolestskinnieinthejungle) I'd like to make a case for substituting Renpy's GOTO aka jump spaghettibait in favor of call where appropriate, and having all the logic that dictates the structure (but not the content) of the story in one .rpy file - this could be in the top level script.rpy or it's own thing.

Benefits:

  • A single file that controls the flow of the story instead of having that control distributed across the 30 or so chapter scripts, which would otherwise all need to know what the subsequent chapter is
  • Ease of inserting, removing and generally managing chapters at a high level with fewer code alterations in fewer places - this benefits modders as well as us
  • Makes large-scale complexity like branching and conditional routes all visible at a macroscopic level rather than hidden in the guts of individual chapters
  • Would make certain bonus features like replaying a single chapter pretty easy to implement

Work to be done:
Actually not that much once the script has been split. Stick a return at the end of each chapter label-block where we would ordinarily jump to the next chapter label, and then define a sequence of calls to each chapter label either directly in the start block at the top level, or in a dedicated script, itself called in the start block:

label start:
    # do init, scores etc.
    call chapter_1
    call chapter_2
    ...
    call chapter_11
    if anonscore >= 4 and fangscore >= 4:
        call chapter_11D
        call chapter_12D
        call chapter_13D
        call chapter_14D
    elif anonscore >= 3 and fangscore <=4:
        call chapter_11C
        call chapter_12C
        call chapter_13C
        call chapter_14C
    elif anonscore <= 3 and fangscore >=3:
        call chapter_11B
        call chapter_12B
        call chapter_13B
        call chapter_14B
    elif anonscore <= 2 and fangscore <= 2:
        call chapter_11A
        call chapter_12A
        call chapter_13A
        call chapter_14A
    else:
    	# error handling
    call ending
    return

Chapter 11 will require a small amount of extra care because we'd be pulling the branch logic and the ending screen out of it.

Anyone's thoughts on this welcome.

After the big refactor lands (my sympathies @coolestskinnieinthejungle) I'd like to make a case for substituting Renpy's [GOTO](https://stackoverflow.com/questions/3517726/what-is-wrong-with-using-goto) aka `jump` spaghettibait in favor of `call` where appropriate, and having all the logic that dictates the structure (but not the content) of the story in one .rpy file - this could be in the top level script.rpy or it's own thing. Benefits: * A single file that controls the flow of the story instead of having that control distributed across the 30 or so chapter scripts, which would otherwise all need to know what the subsequent chapter is * Ease of inserting, removing and generally managing chapters at a high level with fewer code alterations in fewer places - this benefits modders as well as us * Makes large-scale complexity like branching and conditional routes all visible at a macroscopic level rather than hidden in the guts of individual chapters * Would make certain bonus features like replaying a single chapter pretty easy to implement Work to be done: Actually not that much once the script has been split. Stick a `return` at the end of each chapter label-block where we would ordinarily `jump` to the next chapter label, and then define a sequence of `call`s to each chapter label either directly in the `start` block at the top level, or in a dedicated script, itself called in the `start` block: ``` label start: # do init, scores etc. call chapter_1 call chapter_2 ... call chapter_11 if anonscore >= 4 and fangscore >= 4: call chapter_11D call chapter_12D call chapter_13D call chapter_14D elif anonscore >= 3 and fangscore <=4: call chapter_11C call chapter_12C call chapter_13C call chapter_14C elif anonscore <= 3 and fangscore >=3: call chapter_11B call chapter_12B call chapter_13B call chapter_14B elif anonscore <= 2 and fangscore <= 2: call chapter_11A call chapter_12A call chapter_13A call chapter_14A else: # error handling call ending return ``` Chapter 11 will require a small amount of extra care because we'd be pulling the branch logic and the ending screen out of it. Anyone's thoughts on this welcome.

yeah i like this a LOT better
i briefly played with the idea of doing a "timeline" file or whatever like this but didn't think far enough to also use calls and etc.

the branch hasn't been PR'd yet so there's still time to fit this in on that branch instead and agreed this shouldn't take long. hell what took the longest during the initial refactor was fucking getting git and everything set up on my fuckwit linux vm

yeah i like this a LOT better i briefly played with the idea of doing a "timeline" file or whatever like this but didn't think far enough to also use calls and etc. the branch hasn't been PR'd yet so there's still time to fit this in on that branch instead and agreed this shouldn't take long. hell what took the longest during the initial refactor was fucking getting git and everything set up on my fuckwit linux vm

I have never done anything like this in renpy, but in theory this is exactly where this project should be headed.

I have never done anything like this in renpy, but in theory this is exactly where this project should be headed.

This system has its benefits and I can see why it should be used. The jump system is suggested by Renpy though, so keep in mind we'll probably have to explain it in the wiki as a "werid code quirk" or something so renpy newfrens aren't too thrown off.

Anyone know how long this will take to implement? I'm ready for the 28k blob of soul to go bye-bye and all the fixes to be introduced, but then again, whats a bit longer of waiting if it can avoid spaghetti?

Also, no spoilers, but the writers are looking into adding more content to the base game in terms of story, so I think that this would be a welcome change if it makes implementing that much easier.

All in all, this has my approval, but I suggest we don't meyander around too much with it so we can get this monster of a refactor out of the way.

This system has its benefits and I can see why it should be used. The jump system is suggested by Renpy though, so keep in mind we'll probably have to explain it in the wiki as a "werid code quirk" or something so renpy newfrens aren't too thrown off. Anyone know how long this will take to implement? I'm ready for the 28k blob of soul to go bye-bye and all the fixes to be introduced, but then again, whats a bit longer of waiting if it can avoid spaghetti? Also, no spoilers, but the writers are looking into adding more content to the base game in terms of story, so I think that this would be a welcome change if it makes implementing that much easier. All in all, this has my approval, but I suggest we don't meyander around too much with it so we can get this monster of a refactor out of the way.

hell what took the longest during the initial refactor was fucking getting git and everything set up on my fuckwit linux vm

If you're using Windows, use WSL or WSL2 and save yourself the headache of dealing with a VM.
If you're using MacOS, subject yourself to the MacOS terminal space and screw around with homebrew.
No reason to suffer a VM when its not needed.

>hell what took the longest during the initial refactor was fucking getting git and everything set up on my fuckwit linux vm If you're using Windows, use WSL or WSL2 and save yourself the headache of dealing with a VM. If you're using MacOS, subject yourself to the MacOS terminal space and screw around with homebrew. No reason to suffer a VM when its not needed.
PrincipalSpears added the
enhancement
Low Priority
labels 4 months ago

lost track of time but i can have this done and ready for PR merge by tomorrow morning

lost track of time but i can have this done and ready for PR merge by tomorrow morning

changes have been made, now live on my master
https://git.snootgame.xyz/coolestskinnieinthejungle/SnootGame

i think we're finally good to close issue #2 alongside this one

changes have been made, now live on my master https://git.snootgame.xyz/coolestskinnieinthejungle/SnootGame i think we're finally good to close issue https://git.snootgame.xyz/Cavemanon/SnootGame/issues/2 alongside this one
Poster
Collaborator

Thanks for picking this up, u the best. Hopefully it will make life easier down the line.

Its too early in the morning for me to be sure but I think the call start line at the bottom of script.rpy might be redundant since the action of pressing the 'start game' button jumps straight to that label AFAIK.

There may also be a missing return immediately following line 52 in storyline.rpy that will cause the ending to play twice if it's not there.

Thanks for picking this up, u the best. Hopefully it will make life easier down the line. Its too early in the morning for me to be sure but I think the `call start` line at the bottom of script.rpy might be redundant since the action of pressing the 'start game' button jumps straight to that label AFAIK. There may also be a missing `return` immediately following line 52 in storyline.rpy that will cause the ending to play twice if it's not there.

your morning is my 2am lol i'll double check everything you mentioned in like 8-9 hours

your morning is my 2am lol i'll double check everything you mentioned in like 8-9 hours

Just ran through (read used skip) to run through all 4 branches. Hitting all available options.

Initially thought I was going crazy because it was 4am, but I kept running into a bug where after the Dirty Laundry scene it would replay the scene, but only under certain circumstances.

Trying to hit ending 2 (selecting no options that give anon score, and only selecting Fang score options) I hit a bug by selecting "I guess Reed might have a point" and then"ignoring Trish" the next selection (what to do with the flying can) does not seem to affect it right after "Crap, did I leave Saturnia on?" I was sent right back to the Dirty Laundry. Selecting those same options (the 2nd time) again sent me back to the story as normal. I could reproduce this issue consistently.

Ran into the same bug again trying to hit end 4 (selecting only options that gave score) up until the Announcement scene ( I was now testing for it) and could reproduce it again by doing the above. Except in this run, talking to Trish also reproduced the bug.

Have not begun to dig into the code for a possible explanation. If anyone else could replicate this that would be good.

EDIT: Leaving comment as it was originally written, but I don't believe any of the choices in that scene actually matter. Have replicated it now using all available options, once it appears. This is using a fresh clone of the available fork.

save before selection
run into bug
reload and select different option
same bug

Just ran through (read used skip) to run through all 4 branches. Hitting all available options. Initially thought I was going crazy because it was 4am, but I kept running into a bug where after the Dirty Laundry scene it would replay the scene, but only under certain circumstances. Trying to hit ending 2 (selecting no options that give anon score, and only selecting Fang score options) I hit a bug by selecting "I guess Reed might have a point" and then"ignoring Trish" the next selection (what to do with the flying can) does not seem to affect it right after "Crap, did I leave Saturnia on?" I was sent right back to the Dirty Laundry. Selecting those same options (the 2nd time) again sent me back to the story as normal. I could reproduce this issue consistently. Ran into the same bug again trying to hit end 4 (selecting only options that gave score) up until the Announcement scene ( I was now testing for it) and could reproduce it again by doing the above. Except in this run, talking to Trish also reproduced the bug. Have not begun to dig into the code for a possible explanation. If anyone else could replicate this that would be good. EDIT: Leaving comment as it was originally written, but I don't believe any of the choices in that scene actually matter. Have replicated it now using all available options, once it appears. This is using a fresh clone of the available fork. >save before selection >run into bug >reload and select different option >same bug
Poster
Collaborator

@stillzero good work spotting this - I've pinpointed the issue:

947826bd8b/game/script/8.anon-and-fang-study-together.rpy (L930)

Chapter 8 has an early jump to the start of chapter 9, so when that returns, the storyline script still thinks we're in chapter 8 and proceeds to call chapter 9 as if we hadn't just been through it.

PROPOSED FIX:
Easy peasy, replace jump chapter_9 with return. Control is then handed back immediately to the storyline script and it calls chapter 9 as expected.

These kinds of inter-chapter jumps will almost certainly be the root cause of any other similar weirdness we run into during this refactor and need to be found pronto and fixed in the same way (this is assuming the jump is a shortcut to the next chapter - if you find a chapter jump where this isn't the case then please flag it up because it will need to be handled differently).

A quick triage of chapters 1-11 with ctrl+f "chapter" suggests to me that this is the only instance of this bug we have, but I haven't checked the ending branches.

The jump system is suggested by Renpy though, so keep in mind we'll probably have to explain it in the wiki as a "werid code quirk" or something so renpy newfrens aren't too thrown off.

@PrincipalSpears Good point, and vindicated by the sort of teething problem @stillzero just found.

To qualify what I've said in the OP about jump, I think it's ok for low-level control flow inside a chapter (still kind of jank tho) as long as the jumps only ever happen to points within the same chapter .rpy. Contained spaghetti is manageable, uncontained spaghetti waits until you fall asleep and then strangles you.

@stillzero good work spotting this - I've pinpointed the issue: https://git.snootgame.xyz/coolestskinnieinthejungle/SnootGame/src/commit/947826bd8b5b95424b60046c21dcb6a69d0b8063/game/script/8.anon-and-fang-study-together.rpy#L930 Chapter 8 has an early jump to the start of chapter 9, so when *that* returns, the storyline script still thinks we're in chapter 8 and proceeds to call chapter 9 as if we hadn't just been through it. **PROPOSED FIX:** Easy peasy, replace `jump chapter_9` with `return`. Control is then handed back immediately to the storyline script and it calls chapter 9 as expected. These kinds of inter-chapter jumps will almost certainly be the root cause of any other similar weirdness we run into during this refactor and need to be found pronto and fixed in the same way (this is assuming the jump is a shortcut to the **next** chapter - if you find a chapter jump where this isn't the case then please flag it up because it will need to be handled differently). A quick triage of chapters 1-11 with ctrl+f "chapter" suggests to me that this is the only instance of this bug we have, but I haven't checked the ending branches. > The jump system is suggested by Renpy though, so keep in mind we'll probably have to explain it in the wiki as a "werid code quirk" or something so renpy newfrens aren't too thrown off. @PrincipalSpears Good point, and vindicated by the sort of teething problem @stillzero just found. To qualify what I've said in the OP about `jump`, I think it's ok for low-level control flow inside a chapter (still kind of jank tho) **as long as the jumps only ever happen to points within the *same* chapter .rpy**. Contained spaghetti is manageable, uncontained spaghetti waits until you fall asleep and then strangles you.

Excellent work, I decided it was late and I was tired and I'd look at it in the morning and here is a perfectly written explanation already written after my mess of a comment.

That was the only issue I ran into everything else on the surface seemed to work fine with no errors, etc.

Excellent work, I decided it was late and I was tired and I'd look at it in the morning and here is a perfectly written explanation already written after my mess of a comment. That was the only issue I ran into everything else on the surface seemed to work fine with no errors, etc.

@stillzero great find and @Bowie great fix, pretty freakin' sweet

hopefully the save you have should still work and can vet if this fixes that bug (likely should)

code has been updated and i'm pushing right after writing this

@stillzero great find and @Bowie great fix, pretty freakin' sweet hopefully the save you have should still work and can vet if this fixes that bug (likely should) code has been updated and i'm pushing right after writing this

Apologies if you saw the previous comment, I had a couple of BIG BRAIN moments that made this take way longer than it should have.

a.) Yes saves are compatible
b.) is the game closing out after ending 1 intentional?

Other than that. It Just Werks(tm)

Apologies if you saw the previous comment, I had a couple of BIG BRAIN moments that made this take way longer than it should have. a.) Yes saves are compatible b.) is the game closing out after ending 1 intentional? Other than that. It Just Werks(tm)

is the game closing out after ending 1 intentional?

Yep, that's on purpose.

>is the game closing out after ending 1 intentional? Yep, that's on purpose.

I figured it was just double checking.

I figured it was just double checking.

#17 has been merged

https://git.snootgame.xyz/Cavemanon/SnootGame/pulls/17 has been merged
PrincipalSpears closed this issue 4 months ago
PrincipalSpears added this to the Patchy-Patch5 Release milestone 4 months ago
Sign in to join this conversation.
No project
No Assignees
4 Participants
Notifications
Due Date

No due date set.

Dependencies

This issue currently doesn't have any dependencies.

Loading…
There is no content yet.