If you're having trouble or run into any bugs with the code, post a comment here! I will try to get back to you within a few days.
Hello, first of all. Thank you for the time and effort you've dedicated to creating renpy tools and guides. Its wonderful. Next, I was trying out the layered image mask code to see what I could do with it, but I kept running into an issue. Anytime I try to use:
LayeredImageMask()
to define something, I get the following error
AttributeError: 'MultiBox' object has no attribute '_choose_attributes'
would you happen to know why that is?
Oh! Apologies for the delayed response. The error appears when I use
show angel cutin
which is defined as shown in the example on the itch page as
image angel cutin = LayeredImageMask("angel", Transform(crop=(230, 0, 564, 953)), mask="gui/mask.png")The "angel" image is a layered image defined as
image angel = LiveComposite(
(277, 277),
(0, 0), "angelbase@3.png",
(0, 0), "angel normal",
(0, 0), WhileSpeaking("angel", "angel talk", "angelbasemouthclosed@3.png"),
)
However, I also get the error message when I attempt this with a basic png. My renpy version is: 8.3.0.24082114
Thanks again for your time.
The full error message is:
I'm sorry, but an uncaught exception occurred.
While running game code:
File "game/script.rpy", line 680, in script
show angel cutin
File "renpy/common/00layeredimage.rpy", line 1195, in _choose_attributes
return self.filter_attributes(self.image._choose_attributes(tag, attributes, optional))
AttributeError: 'MultiBox' object has no attribute '_choose_attributes'
-- Full Traceback ------------------------------------------------------------
Full traceback:
File "game/script.rpy", line 680, in script
show angel cutin
File "C:\Users\streg\Documents\Visual Novels\renpy-8.3.0-sdk\renpy\ast.py", line 1111, in execute
show_imspec(self.imspec, atl=getattr(self, "atl", None))
File "C:\Users\streg\Documents\Visual Novels\renpy-8.3.0-sdk\renpy\ast.py", line 1074, in show_imspec
renpy.config.show(name,
File "C:\Users\streg\Documents\Visual Novels\renpy-8.3.0-sdk\renpy\exports\displayexports.py", line 474, in show
name, what = _find_image(layer, key, name, what)
File "C:\Users\streg\Documents\Visual Novels\renpy-8.3.0-sdk\renpy\exports\displayexports.py", line 278, in _find_image
new_image = renpy.game.context().images.apply_attributes(layer, key, name)
File "C:\Users\streg\Documents\Visual Novels\renpy-8.3.0-sdk\renpy\display\image.py", line 990, in apply_attributes
return self.choose_image(nametag, required, optional, name)
File "C:\Users\streg\Documents\Visual Novels\renpy-8.3.0-sdk\renpy\display\image.py", line 1011, in choose_image
newattrs = ca(tag, ca_required, ca_optional)
File "renpy/common/00layeredimage.rpy", line 1195, in _choose_attributes
return self.filter_attributes(self.image._choose_attributes(tag, attributes, optional))
AttributeError: 'MultiBox' object has no attribute '_choose_attributes'
Windows-10-10.0.19044 AMD64
Ren'Py 8.3.0.24082114
HEATWAVE MINIMUM WAGE MAXIMUM DEBT 1.0
Mon Oct 7 10:23:32 2024
The problem is your angel image is not a layered image - if you don't have a layered image you'd just use regular AlphaMask and not the LayeredImageMask. Layered images are declared like layeredimage angel and have a specific format that Ren'Py expects. I suggest you look into it, as it's the modern and simpler way to put together images with multiple layers. https://www.renpy.org/doc/html/layeredimage.html#LayeredImage
If your question is whether you can use a defined image displayable instead of a layered image - the answer is no, that's what a regular AlphaMask displayable is for. If your question is if you can use a defined image displayable for the mask image, sure! LayeredImageMask takes any kind of displayable for the mask, just like AlphaMask does.
Sure! It's a bit of a weird setup, but you can use a regular function as the Transform argument to LayeredImageMask. Here's an example:
image fen_masked = LayeredImageMask("feniks", lambda x : At(x, adjust_crop), mask="mask2", background="bg2")
transform adjust_crop:
animation
crop (0, 0, 637, 431)
linear 2.0 crop (1171-637, 950-431, 637, 431)
linear 2.0 crop (0, 0, 637, 431)
repeat
label start:
"Start"
show fen_masked at truecenter
"Tada"
show fen_masked sparkle animated right2
"Sparkle"
The important part is the lambda - that's a function. You could also make a regular function that returns At(<img>, <transform>) yourself and set that as the second argument to LayeredImageMask. The `animation` property in the transform is also important so that the transform doesn't reset for every new expression change. Hope that helps!
SO. first off. please please please do simply disregard me if you don't wanna deal with this, since it concerns someone else's extension/a weird implementation of your thing. 😎✨ i'm still a baby coder so i'm not sure how easy or laborious this may or may not be but it FEELS like im just having a syntax problem. ....potentially with an obvious answer right in my face (that's often the case with me lmfao)
i've been trying to figure out how to use this in conjunction with Auto Highlight by Wattson. For some scenes, I want to mask the layered character sprites onto boxes, like so:
image erikabox = LayeredImageMask("erika",
Transform(crop=(120, 170, 500, 500),zoom=0.7),
mask="masks/blankbox_ghost.png",
foreground="transparent.png",
background="masks/blankbox_ghost.png",
)

the boxes look like this ingame. when a character is speaking, their box brightens and zooms in; when another character speaks, it dims and zooms out. it works great on regular masked images. mr. suspicious on the right there is just a placeholder image masked with the torn-paper-looking box, and it works on him. but it's been a conundrum trying to figure out how to get it to work on a masked layered image (ms. ghost on the left)
here's part of the instructions from the auto highlight script:
# - Second, you'll need to apply the sprite_highlight transform to all images you want this
# applied to. For people using layeredimages, this is very easy. As an example:
# layeredimage eileen:
# at sprite_highlight('eileen')
# ...
# - However, if you're using individual sprites, you'll have to be sure this is applied to every one.
# image eileen happy = At('eileen_happy', sprite_highlight('eileen'))
# image eileen sad = At('eileen_sad', sprite_highlight('eileen'))
# Or, if you'd prefer an ATL example
# image eileen happy:
# 'eileen_happy'
# function SpriteFocus('eileen')
if i try putting it into the layeredimage, it won't highlight the whole box correctly... but when i try to cram function SpriteFocus('erikabox') anywhere into the LayeredImageMask statement, i get nothing but errors. neither of these work and i don't really know what else to try.
#nope
image erikabox = LayeredImageMask("erika",
Transform(crop=(120, 170, 500, 500),zoom=0.7),
mask="masks/blankbox_ghost.png",
foreground="transparent.png",
background="masks/blankbox_ghost.png",
function SpriteFocus('erikabox')
)
# yeah i didn't expect this to work lol
image erikabox = LayeredImageMask("erika",
Transform(crop=(120, 170, 500, 500),zoom=0.7),
mask="masks/blankbox_ghost.png",
foreground="transparent.png",
background="masks/blankbox_ghost.png"
)
function SpriteFocus('erikabox')
ANYWAY... tl;dr do you think there's any way to sneak that function into the LayeredImageMask??? also regardless of anything THANK YOU for my life, your ren'py extensions and tutorials are THE BEE'S KNEES
I haven't downloaded that extension to check, but you should be able to make an ATL transform that handles it. For example, the following code will apply the highlighting to the layered image:
transform highlight_mask(child, crop=(0.0, 0.0, 1.0, 1.0), zoom=1.0, who='eileen'):
crop crop zoom zoom
function SpriteFocus(who)
image fen mask_highlight = LayeredImageMask("feniks", highlight_mask(crop=(0.2, 0.2, 0.5, 0.5), zoom=0.7, who="fen"), mask=..., foreground=...)If you want the highlighting to apply to the background/foreground too, then you should be able to use At(), so like, background=At("whatsit.png", highlight_mask(who="fen")) and that in theory should highlight the background too.
Hope that works!