Need help implementing conditional compilation

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Message
Author
User avatar
AVNSnax
Regular
Posts: 35
Joined: Sun Feb 06, 2022 12:11 am
itch: avnsnax
Contact:

Need help implementing conditional compilation

#1 Post by AVNSnax » Fri Jun 10, 2022 10:07 am

The renpy.org docs touch on this in places, but I can't find a definitive explanation of exactly what loads, when.

What I'm trying to do is implement a sort of rudimentary conditional compilation system. (With the least amount of effort, cough, cough) I've had requests for a compressed images version of my VN which is fine, but I don't want to have to maintain multiple parallel versions of the source. So I thought, that's simple enough: I'll just implement a flag that determines which image files are defined at runtime. And, of course, add logic to add the appropriate files to archives at build time.

Code: Select all

define COMP_IMAGES = True
. . .
if COMP_IMAGES:
    image test = "test.jpg"
else:
    image test = "test.webp"
Except it appears that jpeg versions of the images never get defined, regardless of COMP_IMAGES.

Okay, I think, must be loading order issues and COMP_IMAGES isn't being defined early enough. So, in a separate file, I slap an init python statement before the COMP_IMAGES definition, choosing an insanely low number in order to guarantee it's loaded first:

Code: Select all

init -10000 python:
    COMP_IMAGES = True
Doesn't seem to make a difference. So I put a print statement in the images definition file:

Code: Select all

$ print(COMP_IMAGES)
if COMP_IMAGES:
    image test = "test.jpg"
else:
    image test = "test.webp"
Nothing ever gets printed. I was about to dig into the Ren'Py source directly but thought better of it and came here to ask first.

I get that init python and init offset force sections of code to run in a particular order, but when, and in what relation to the gui starting up? Seems like it should be real simple, but it's not turning out that way. Am I just overthinking this?

User avatar
zmook
Veteran
Posts: 421
Joined: Wed Aug 26, 2020 6:44 pm
Contact:

Re: Need help implementing conditional compilation

#2 Post by zmook » Fri Jun 10, 2022 10:30 am

I *think* what is happening here is that 'image' statements are pulled out and run at init time, while the "if/else" statement is a runtime construct that ends up being ignored. Effectively renpy considers your code equivalent to this, I think:

Code: Select all

image test = "test.jpg"
image test = "test.webp"
if COMP_IMAGES:
else:
Also, in

Code: Select all

$ print(COMP_IMAGES)
the '$' makes a runtime statement, so it will only get executed if control flow passes over that line while playing the game. Probably you wanted this instead:

Code: Select all

init python:
    print(COMP_IMAGES)
Regardless, it doesn't sound like declaring image statements will do what you want anyway. All that will determine is which version of an image is *used* at runtime, when presumably the point of a compressed-image build is that it's smaller to download. If you include *both* copies of the images, you've made your build bigger, not smaller.

The way to include only one copy of your images is with build.classify declarations -- see the bottom of your options.rpy file. Something like:

Code: Select all

if COMP_IMAGES:
    build.classify('game/**.jpg', 'compressed_images')
else:
    build.classify('game/**.webp', 'images')
Renpy is already pretty smart about finding images, so if you just leave off the 'jpg' and 'webp' suffixes of your 'image' declaration statements, at runtime it will find whichever version was included.
colin r
➔ if you're an artist and need a bit of help coding your game, feel free to send me a PM

User avatar
AVNSnax
Regular
Posts: 35
Joined: Sun Feb 06, 2022 12:11 am
itch: avnsnax
Contact:

Re: Need help implementing conditional compilation

#3 Post by AVNSnax » Fri Jun 10, 2022 11:17 am

zmook wrote:
Fri Jun 10, 2022 10:30 am
I *think* what is happening here is that 'image' statements are pulled out and run at init time, while the "if/else" statement is a runtime construct that ends up being ignored.
Hence, my question about what gets loaded when. The image assignments are in a standard .rpy file; I'd expect code there to execute as it would elsewhere. I get that the image statement is just a construct, but that doesn't make it any easier to visualize.

I didn't know about Ren'Py looking for other image types. I'm an old school developer which is why a lot of this automatic, behind-the-scenes behavior eludes me. I'm constantly frustrated by being told "why didn't you just do it this way?" when "this way" isn't documented anywhere and you just kinda have to know already. The old "I don't know what I don't know" adage applies here, in spades. Ren'Py's flexible syntax doesn't help either--while it makes for easier-to-read (?) code, it reminds me of the old days when developers would write intricate precompiler macros that they, and only they, understood the behavior for. If you're the person coming in behind them to maintain the code base, heaven help you. Worse, undefined default behavior (by undefined, I mean not explicitly documented) can cause all sorts of bugs to creep in as you write code that just magically works without understanding why, only to have similar code fail in another place. At least with a precompiler, I can see what code ends up being emitted to compile in order to debug.

Sorry for the soapbox rant. Ren'Py is an incredibly powerful system. I just wish it was (much) better documented, and I don't just mean just someone else's example code to look at. I was already doing the correct thing with build_classify--Ren'Py just couldn't find the files it was looking for. I'll try your suggestion and see how it works.

User avatar
Ocelot
Eileen-Class Veteran
Posts: 1882
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Need help implementing conditional compilation

#4 Post by Ocelot » Fri Jun 10, 2022 11:45 am

Technically it is documented, but in really unexpected places. You pretty much need to know what are you looking for or read whole documentation multiple times.

Execution order is described there: https://www.renpy.org/doc/html/python.h ... -statement
When a priority is not given, 0 is used. Init statements are run in priority order, from lowest to highest. Init statements of the same priority are run in Unicode order by filename, and then from top to bottom within a file.

To avoid conflict with Ren'Py, creators should use priorities in the range -999 to 999. Priorities of less than 0 are generally used for libraries and to set up themes. Normal init statements should have a priority of 0 or higher.
And that image statements are always run at init time there: https://www.renpy.org/doc/html/displayi ... -statement
The image statement is run at init time, before the menus are shown or the start label runs. When not contained inside an init block, image statements are run as if they were placed inside an init block of priority 500.
One thing that is not mentioned is that init blocks (or statements that run in init phase) can be placed virtually anywhere where normal statement can be placed. Which could lead to surprising behavior (since non-init code is completely ignored during initialization and init statements are skipped during normal execution)
< < insert Rick Cook quote here > >

User avatar
AVNSnax
Regular
Posts: 35
Joined: Sun Feb 06, 2022 12:11 am
itch: avnsnax
Contact:

Re: Need help implementing conditional compilation

#5 Post by AVNSnax » Fri Jun 10, 2022 12:39 pm

zmook wrote:
Fri Jun 10, 2022 10:30 am
Renpy is already pretty smart about finding images, so if you just leave off the 'jpg' and 'webp' suffixes of your 'image' declaration statements, at runtime it will find whichever version was included.
Ahhh, no. Ren'Py reports "Image 'blah' not found." I ensured that the jpg files were in the same directories as their webp counterparts and were included in the build via build.classify (I can see the jpg files in the right places). I left off the file extensions in the image declarations.

Any other suggestions?

User avatar
AVNSnax
Regular
Posts: 35
Joined: Sun Feb 06, 2022 12:11 am
itch: avnsnax
Contact:

Re: Need help implementing conditional compilation

#6 Post by AVNSnax » Fri Jun 10, 2022 12:52 pm

Ocelot wrote:
Fri Jun 10, 2022 11:45 am
Execution order is described there: https://www.renpy.org/doc/html/python.h ... -statement
When a priority is not given, 0 is used. Init statements are run in priority order, from lowest to highest. Init statements of the same priority are run in Unicode order by filename, and then from top to bottom within a file.
I did miss the documentation about the Image statement. But the execution order docs describe how init statements are prioritized in and amongst themselves, but not how they relate to other initializations occurring seemingly by happenstance. I really believe that somewhere it should be documented exactly what order things happen at load and run times. Docs are spread out all over the place, if it's mentioned at all.

User avatar
zmook
Veteran
Posts: 421
Joined: Wed Aug 26, 2020 6:44 pm
Contact:

Re: Need help implementing conditional compilation

#7 Post by zmook » Fri Jun 10, 2022 1:05 pm

AVNSnax wrote:
Fri Jun 10, 2022 11:17 am
I'm constantly frustrated by being told "why didn't you just do it this way?" when "this way" isn't documented anywhere and you just kinda have to know already.
I hear your pain. The docs can be obtuse. Personally I wish they could be a wiki so I could clarify the parts that I beat my head against, but I guess once upon a time they *were* a wiki, and got spam-harassed into the current frozen state. On the upside, this forum is really helpful.
AVNSnax wrote:
Fri Jun 10, 2022 12:39 pm
Ahhh, no. Ren'Py reports "Image 'blah' not found." I ensured that the jpg files were in the same directories as their webp counterparts and were included in the build via build.classify (I can see the jpg files in the right places). I left off the file extensions in the image declarations.
Yeah, okay, it's kind of obtuse and I wish it was more clearly specified also. I *think* the rule is:

Code: Select all

	show eileen_idle	# implicitly looks up a file named "eileen_idle.jpg", "eileen_idle.png" or "eileen_idle.webp"
	show "eileen_idle.jpg"	# if you quote it, must match the actual filename, though it still searches subdirectories
	show "eileen_idle" 	# error: expected 'image_name_component' not found
If the implicit lookup is too frustrating, you could always make it explicit:

Code: Select all

init python:
   def f(name):
   	return name + (".jpg" if COMP_IMAGES else ".webp")
   	
label start:
    show f("eileen_idle")
colin r
➔ if you're an artist and need a bit of help coding your game, feel free to send me a PM

User avatar
zmook
Veteran
Posts: 421
Joined: Wed Aug 26, 2020 6:44 pm
Contact:

Re: Need help implementing conditional compilation

#8 Post by zmook » Fri Jun 10, 2022 1:18 pm

AVNSnax wrote:
Fri Jun 10, 2022 12:52 pm
I did miss the documentation about the Image statement. But the execution order docs describe how init statements are prioritized in and amongst themselves, but not how they relate to other initializations occurring seemingly by happenstance. I really believe that somewhere it should be documented exactly what order things happen at load and run times. Docs are spread out all over the place, if it's mentioned at all.
Almost everything either happens at init time or run time. Off the top of my head, "init", "define", "image", and "transform" are init time statements. All script files are scanned for them, they're pulled out, sorted by priority, and run when the game is loaded. Runtime statements are basically everything else. They're executed in sequence starting from "label start:" when you Start() is called (if you ignore the game menus).

'screen's might be parsed at init time, but I don't believe they're initialized until the predictor encounters them at run time.

There's probably a couple exceptions from that, but I can't think what they might be.
colin r
➔ if you're an artist and need a bit of help coding your game, feel free to send me a PM

User avatar
Ocelot
Eileen-Class Veteran
Posts: 1882
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Need help implementing conditional compilation

#9 Post by Ocelot » Fri Jun 10, 2022 1:20 pm

AVNSnax wrote:
Fri Jun 10, 2022 12:52 pm
I did miss the documentation about the Image statement. But the execution order docs describe how init statements are prioritized in and amongst themselves, but not how they relate to other initializations occurring seemingly by happenstance.
I noticed that section about init phase disappeared from documentation for some reason some time ago. If you are interested in how game executes during startup, here is the short version:

First, statements init phase statements are executed. This includes init, init python, image, define, default, transform, screen, style, translate, and other statements for which it is documented that they are run in init phase. I posted order of init statement execution earlier. Generally most of RenPy itself initialized with init order <-1000, and you are not supposed to use those priority orders yourself. However, some things should be run at specific "early" init order, but all those things are clearly documented (they mostly introduce your own statements to RenPy parser).

Init phase happens before splashscreen or main menu is shown, but after presplash image was displayed (if there is one).

After that, before_main_menu label is called if present.

Then main menu is displayed using one of displayed ways (by calling main_menu label, if present, otherwise displaying main_menu screen).

After you start the game control is transfered to start label (or any other, if specified otherwise). At that moment all init statement had already run and no other code was executed. From now on, only code encountered during execution will be run.
< < insert Rick Cook quote here > >

User avatar
AVNSnax
Regular
Posts: 35
Joined: Sun Feb 06, 2022 12:11 am
itch: avnsnax
Contact:

Re: Need help implementing conditional compilation

#10 Post by AVNSnax » Fri Jun 10, 2022 1:34 pm

zmook wrote:
Fri Jun 10, 2022 1:05 pm
Yeah, okay, it's kind of obtuse and I wish it was more clearly specified also. I *think* the rule is:

Code: Select all

	show eileen_idle	# implicitly looks up a file named "eileen_idle.jpg", "eileen_idle.png" or "eileen_idle.webp"
	show "eileen_idle.jpg"	# if you quote it, must match the actual filename, though it still searches subdirectories
	show "eileen_idle" 	# error: expected 'image_name_component' not found
Well, all of my images are shown using Image names without quotes, so that's not it. They're not all stored directly in the images directory--I've broken them up into scene folders just to keep my sanity trying to navigate thousands of files. But that doesn't appear to matter either.
If the implicit lookup is too frustrating, you could always make it explicit:

Code: Select all

init python:
   def f(name):
   	return name + (".jpg" if COMP_IMAGES else ".webp")
   	
label start:
    show f("eileen_idle")
Yeah, that's what I was afraid of from the beginning. I also have a few custom python routines that handle special effects during scene and show statements that require me to pass the image name via text (not my doing--renpy.scene and renpy.show behavior) so I guess I'd have to either declare them all as standard show statements and figure out how to duplicate the behavior there. Sigh. I was hoping this was a one-day exercise, but it's a lot more complicated than I'd originally thought.
Last edited by AVNSnax on Fri Jun 10, 2022 1:45 pm, edited 3 times in total.

User avatar
AVNSnax
Regular
Posts: 35
Joined: Sun Feb 06, 2022 12:11 am
itch: avnsnax
Contact:

Re: Need help implementing conditional compilation

#11 Post by AVNSnax » Fri Jun 10, 2022 1:36 pm

Ocelot wrote:
Fri Jun 10, 2022 1:20 pm
First, statements init phase statements are executed. This includes init, init python, image, define, default, transform, screen, style, translate, and other statements for which it is documented that they are run in init phase. I posted order of init statement execution earlier. Generally most of RenPy itself initialized with init order <-1000, and you are not supposed to use those priority orders yourself. However, some things should be run at specific "early" init order, but all those things are clearly documented (they mostly introduce your own statements to RenPy parser).

Init phase happens before splashscreen or main menu is shown, but after presplash image was displayed (if there is one).

After that, before_main_menu label is called if present.

Then main menu is displayed using one of displayed ways (by calling main_menu label, if present, otherwise displaying main_menu screen).

After you start the game control is transfered to start label (or any other, if specified otherwise). At that moment all init statement had already run and no other code was executed. From now on, only code encountered during execution will be run.
Thanks ever so much for that explanation. That's going into my docs immediately. Still not wholly comprehensive, but a helluva lot better than I'd seen before. Thanks again.

User avatar
Ocelot
Eileen-Class Veteran
Posts: 1882
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Need help implementing conditional compilation

#12 Post by Ocelot » Fri Jun 10, 2022 1:41 pm

You technically can generate image names at startup:

Code: Select all

init -999 python:
    # you can check existence if single .jpg file exist and set extention as you want.
    # if it is runtime speed problem and not game size problem, you can make a menu choice, which images you want to use
    # and store user choice in persistent variables 
    $ image_ext = "jpg" if (some condition) else "webp"

image bg stars = "backgrounds/fullscreen/chapter1/stars.%s" % image_ext
< < insert Rick Cook quote here > >

User avatar
zmook
Veteran
Posts: 421
Joined: Wed Aug 26, 2020 6:44 pm
Contact:

Re: Need help implementing conditional compilation

#13 Post by zmook » Fri Jun 10, 2022 1:53 pm

AVNSnax wrote:
Fri Jun 10, 2022 1:34 pm
Well, all of my images are "show"n using Image names without quotes, so that's not it. They're not all stored directly in the images directory--I've broken them up into scene folders just to keep my sanity trying to navigate thousands of files
By "not directly in the images directory", you mean they're in subdirectories of "images", right?

The documented behavior is:
The image directory is named "images", and is placed under the game directory. When a file with the .jpg or .png extension is placed underneath this directory, the extension is stripped, the rest of the filename is forced to lowercase, and the resulting filename is used as the image name if an image with that name has not been previously defined.
(This specifies jpg or png only, but in my experience it does do webp also.) To make that explicit, my understanding is that if you have a file "game/images/ch1/eileen/eileen_dancing.jpg", ren'py will implicitly execute this statement for you at init time:

Code: Select all

image eileen_dancing = "game/images/ch1/eileen/eileen_dancing.jpg"
so then the name "eileen_dancing" exists as an image name and you should be able to use it straight up as

Code: Select all

show eileen_dancing
If this isn't working, what error do you get exactly?

You can make renpy tell you what images it knows about by opening the console and running

Code: Select all

renpy.list_images()
colin r
➔ if you're an artist and need a bit of help coding your game, feel free to send me a PM

User avatar
Ocelot
Eileen-Class Veteran
Posts: 1882
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Need help implementing conditional compilation

#14 Post by Ocelot » Fri Jun 10, 2022 2:01 pm

zmook wrote:
Fri Jun 10, 2022 1:53 pm
so then the name "eileen_dancing" exists as an image name and you should be able to use it straight up as

Code: Select all

show eileen_dancing
If this isn't working, what error do you get exactly?

You can make renpy tell you what images it knows about by opening the console and running

Code: Select all

renpy.list_images()
You miss the part where image name is broken into tag and attributes by known separators. Underscore is one of them. Defined image name would be 'eileen dancing' without underscore.
< < insert Rick Cook quote here > >

User avatar
zmook
Veteran
Posts: 421
Joined: Wed Aug 26, 2020 6:44 pm
Contact:

Re: Need help implementing conditional compilation

#15 Post by zmook » Fri Jun 10, 2022 2:08 pm

Ocelot wrote:
Fri Jun 10, 2022 2:01 pm
You miss the part where image name is broken into tag and attributes by known separators. Underscore is one of them. Defined image name would be 'eileen dancing' without underscore.
The game I'm working on contains a file "carla_dori_tmp.webp". In console:

Code: Select all

> [x for x in renpy.list_files() if 'carla' in x]
['carla neutral', 'carla neutral speaking', 'carla_dori_tmp']
The first two are from explicit 'image' statements in my code; the third is the one renpy implicitly created for me. Note that it did *not* split on underscores.

The parsing rules are different for layeredimages, if that's what you're thinking of.
colin r
➔ if you're an artist and need a bit of help coding your game, feel free to send me a PM

Post Reply

Who is online

Users browsing this forum: Bing [Bot]