Ren'Py Creating a dynamically composited image

Epadder

Programmer
Game Developer
Oct 25, 2016
568
1,058
So the problem is I wanted to create a dynamically composited image that would automatically calculate the position of the two 'side mirrors', I have it working if I just input the values for a 960 pixel width image, but some images aren't 16:9 1280 x 720 or 4:3 960x720.

I could just either edit the offending images or make a different choice (my current project is like Haramase Sim and uses images from existing Japanese games), but I'd really like to not have to do that ;).

Trying to use a default variable in the image code gives me the error:
Code:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/special-images.rpy", line 77, in script
    image dynamicimage_victoryscene_composite = Composite(
  File "game/special-images.rpy", line 80, in <module>
    (1280 - dynamicimage_width,0), "dynamicimage_victoryscene",
NameError: name 'dynamicimage_width' is not defined

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\bootstrap.py", line 313, in bootstrap
    renpy.main.main()
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\main.py", line 423, in main
    renpy.game.context().run(node)
  File "game/special-images.rpy", line 77, in script
    image dynamicimage_victoryscene_composite = Composite(
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\ast.py", line 980, in execute
    img = renpy.python.py_eval_bytecode(self.code.bytecode)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\python.py", line 1937, in py_eval_bytecode
    return eval(bytecode, globals, locals)
  File "game/special-images.rpy", line 80, in <module>
    (1280 - dynamicimage_width,0), "dynamicimage_victoryscene",
NameError: name 'dynamicimage_width' is not defined

Windows-8-6.2.9200
Ren'Py 7.1.3.1092
Adam and Gaia : THXE (Turbo Hyper Extreme Edition) Foundation
Thu Dec 13 23:43:19 2018
Here are the relevant snippets of code:
The Image Definition:
Python:
image dynamicimage_victoryscene = "battlechars/[rpgstat_EnemyRandomType]/[rpgstat_EnemyRandomType]_[dynamicimage_victory]/[rpgstat_EnemyRandomType]_[dynamicimage_victory] [dynamicimage_currentstep].webp"
image dynamicimage_victoryscene_composite = Composite(
    (1280,720),
    (0,0), "dynamicimage_victoryscene",
    (1280 - dynamicimage_width,0), "dynamicimage_victoryscene",
    (0,0), "shaded.webp",
    ((1280 - dynamicimage_width) / 2,0), "dynamicimage_victoryscene"
    )
The Victory Scene:
Python:
label hscenelabel_SlimeGirlVictoryScene:
    stop music
    ### REDACTED ####
    $ dynamicimage_victory = "{:02}".format(renpy.random.randint(1,27))
    $ dynamicimage_width = rpgfunction_getimagesize()
    scene scene_bg with dissolve
    ### REDACTED ####
    if dynamicimage_width < 1280:
        show dynamicimage_victoryscene_composite at transform_IncreaseSizeBy50Percent,transform_CenterInScreen, transformfunction_VictoryScene() with dissolve
    else:
        show dynamicimage_victoryscene at transform_IncreaseSizeBy50Percent, transform_CenterInScreen, transformfunction_VictoryScene() with dissolve
    ### REDACTED ####
    $ dynamicimage_currentstep = "02"
    $ renpy.with_statement(dissolve)
    ### REDACTED ####
    $ dynamicimage_currentstep = "03"
    $ renpy.with_statement(dissolve)
    ### REDACTED ####
    $ dynamicimage_currentstep = "02"
    $ renpy.with_statement(dissolve)
    ### REDACTED ####
    $ dynamicimage_currentstep = "04"
    $ renpy.with_statement(dissolve)
    show whiteflash zorder 50
    ### REDACTED ####
    $ dynamicimage_currentstep = "05"
    $ renpy.with_statement(dissolve)
    ### REDACTED ####
    scene scene_bg with dissolve
    ### REDACTED ####
    jump functionlabel_AfterHSceneHub
How I'm getting the image size:
Python:
def rpgfunction_getimagesize():
    dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
    iSize = renpy.image_size(dynamicimage)
    return iSize[0]
You don't have permission to view the spoiler content. Log in or register now.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,294
15,156
Python:
image dynamicimage_victoryscene_composite = Composite(
    (1280,720),
    (0,0), "dynamicimage_victoryscene",
    (1280 - dynamicimage_width,0), "dynamicimage_victoryscene",
    (0,0), "shaded.webp",
    ((1280 - dynamicimage_width) / 2,0), "dynamicimage_victoryscene"
    )
Composite don't works like you think it do. The code above will be executed by Ren'py at init time. Will doing this, Ren'py will try to compute "1280 - dynamicimage_width", and fail because the value isn't defined yet.

What you wanted to do is this :
Python:
image dynamicimage_victoryscene_composite = Composite(
    (1280,720),
    (0,0), "dynamicimage_victoryscene",
    (rpgfunction_getimagesize, 0), "dynamicimage_victoryscene",
    (0,0), "shaded.webp",
    ((rpgfunction_getimagesize) / 2,0), "dynamicimage_victoryscene"
    )
with rpgfunction_getimagesize rewrote to return "1280 - iSize[0]".

This said, I'm not sure that Composite/LiveComposite accept callable as value for the size. So you perhaps need to use :
Python:
image dynamicimage_victoryscene_composite = Composite(
    (1280,720),
    (0,0), "dynamicimage_victoryscene",
    rpgfunction_getimagesize, "dynamicimage_victoryscene",
    (0,0), "shaded.webp",
    rpgfunction_getimagesizeDivided, "dynamicimage_victoryscene"
    )
with rpgfunction_getimagesize and rpgfunction_getimagesizeDivided this time returning the whole tuple. Yet, I'm not sure either that it accept callable as size.

There's also the option to works with for the size. But it's a callable, so if it works with it, at least one of the solution above should also work.
 

Epadder

Programmer
Game Developer
Oct 25, 2016
568
1,058
When I try to use the functions I get a new error :(:
Code:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/battle-goo.rpy", line 11, in script
    show dynamicimage_victoryscene_composite at transform_IncreaseSizeBy50Percent,transform_CenterInScreen, transformfunction_VictoryScene() with dissolve
TypeError: unsupported operand type(s) for +: 'function' and 'int'

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "game/battle-goo.rpy", line 11, in script
    show dynamicimage_victoryscene_composite at transform_IncreaseSizeBy50Percent,transform_CenterInScreen, transformfunction_VictoryScene() with dissolve
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\ast.py", line 1356, in execute
    renpy.exports.with_statement(trans, paired)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\exports.py", line 1481, in with_statement
    return renpy.game.interface.do_with(trans, paired, clear=clear)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\core.py", line 2227, in do_with
    clear=clear)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\core.py", line 2672, in interact
    repeat, rv = self.interact_core(preloads=preloads, trans_pause=trans_pause, **kwargs)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\core.py", line 3158, in interact_core
    self.draw_screen(root_widget, fullscreen_video, (not fullscreen_video) or video_frame_drawn)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\core.py", line 2075, in draw_screen
    renpy.config.screen_height,
  File "render.pyx", line 487, in renpy.display.render.render_screen
  File "render.pyx", line 235, in renpy.display.render.render
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\layout.py", line 722, in render
    surf = render(child, width, height, cst, cat)
  File "render.pyx", line 147, in renpy.display.render.render
  File "render.pyx", line 235, in renpy.display.render.render
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\transition.py", line 361, in render
    top = render(self.new_widget, width, height, st, at)
  File "render.pyx", line 147, in renpy.display.render.render
  File "render.pyx", line 235, in renpy.display.render.render
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\layout.py", line 722, in render
    surf = render(child, width, height, cst, cat)
  File "render.pyx", line 147, in renpy.display.render.render
  File "render.pyx", line 235, in renpy.display.render.render
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\layout.py", line 722, in render
    surf = render(child, width, height, cst, cat)
  File "render.pyx", line 147, in renpy.display.render.render
  File "render.pyx", line 235, in renpy.display.render.render
  File "accelerator.pyx", line 110, in renpy.display.accelerator.transform_render
  File "render.pyx", line 235, in renpy.display.render.render
  File "accelerator.pyx", line 110, in renpy.display.accelerator.transform_render
  File "render.pyx", line 235, in renpy.display.render.render
  File "accelerator.pyx", line 110, in renpy.display.accelerator.transform_render
  File "render.pyx", line 235, in renpy.display.render.render
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\image.py", line 500, in render
    return wrap_render(self.target, width, height, st, at)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\image.py", line 306, in wrap_render
    rend = render(child, w, h, st, at)
  File "render.pyx", line 147, in renpy.display.render.render
  File "render.pyx", line 235, in renpy.display.render.render
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\layout.py", line 735, in render
    offset = child.place(rv, 0, 0, width, height, surf)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\core.py", line 590, in place
    xpos, ypos = place(width, height, surf.width, surf.height, placement)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\core.py", line 199, in place
    x = xpos + xoffset - xanchor
TypeError: unsupported operand type(s) for +: 'function' and 'int'

Windows-8-6.2.9200
Ren'Py 7.1.3.1092
Adam and Gaia : THXE (Turbo Hyper Extreme Edition) Foundation
Fri Dec 14 10:53:11 2018
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,294
15,156
When I try to use the functions I get a new error :(:
Code:
TypeError: unsupported operand type(s) for +: 'function' and 'int'
It just mean that Ren'py don't accept a callable here. I warned about it, I wasn't sure that it will accept it.

Now, the important question is: You get this with which method ?
Reading the trace, it seem to be the first one, but I'm not 100% sure. So, does it happen for both method, or have you only tried the first one ?
 

Epadder

Programmer
Game Developer
Oct 25, 2016
568
1,058
I only did the first one, I wasn't quite sure what returning the whole tuple would accomplish for my scenario. As I understand it the tuple it expects in the composite is the anchor point of the image, so supplying the image height would anchor it at the bottom of the composite.

Anyway I tried the other way trying to get the right anchor point:
Python:
    def rpgfunction_GetImageWidthDifference():
        dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
        iSize = renpy.image_size(dynamicimage)
        return (1280 - iSize[0], iSize[1] - 720)

    def rpgfunction_GetImageWidthDifferenceHalved():
        dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
        iSize = renpy.image_size(dynamicimage)
        return ((1280 - iSize[0]) / 2, iSize[1] - 720)
Python:
image dynamicimage_victoryscene = "battlechars/[rpgstat_EnemyRandomType]/[rpgstat_EnemyRandomType]_[dynamicimage_victory]/[rpgstat_EnemyRandomType]_[dynamicimage_victory] [dynamicimage_currentstep].webp"
image dynamicimage_victoryscene_composite = Composite(
    (1280,720),
    (0,0), "dynamicimage_victoryscene",
    rpgfunction_GetImageWidthDifference, "dynamicimage_victoryscene",
    (0,0), "shaded.webp",
    rpgfunction_GetImageWidthDifferenceHalved, "dynamicimage_victoryscene"
    )
Then I get a different error:

Code:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/special-images.rpy", line 77, in script
    image dynamicimage_victoryscene_composite = Composite(
  File "game/special-images.rpy", line 82, in <module>
    rpgfunction_GetImageWidthDifferenceHalved, "dynamicimage_victoryscene"
TypeError: 'function' object is not iterable

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\bootstrap.py", line 313, in bootstrap
    renpy.main.main()
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\main.py", line 423, in main
    renpy.game.context().run(node)
  File "game/special-images.rpy", line 77, in script
    image dynamicimage_victoryscene_composite = Composite(
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\ast.py", line 980, in execute
    img = renpy.python.py_eval_bytecode(self.code.bytecode)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\python.py", line 1937, in py_eval_bytecode
    return eval(bytecode, globals, locals)
  File "game/special-images.rpy", line 82, in <module>
    rpgfunction_GetImageWidthDifferenceHalved, "dynamicimage_victoryscene"
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\layout.py", line 302, in Composite
    xpos, ypos = pos
TypeError: 'function' object is not iterable

Windows-8-6.2.9200
Ren'Py 7.1.3.1092
Adam and Gaia : THXE (Turbo Hyper Extreme Edition) Foundation
Fri Dec 14 14:03:43 2018
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,294
15,156
Code:
TypeError: 'function' object is not iterable
Fuck. So, it's now clear, Composite don't works with callable. And it will not works with ConditionSwitch for the same reason :(

The only solution is to make your own iterable :
Code:
init python:

    class Rpgfunction_getimagesize( renpy.python.RevertableObject ):
        def __init__( self, isDivided = False ):
            self.isDivided = isDivided 
            self.__step = -1

        def __iter__( self ):   
            self.__step = -1
            return self

        def next( self ):   
            self.__step += 1
            if self.__step == 0:
                dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
                return config.screen_width - renpy.image_size(dynamicimage)[0] if isDivided is False else ( config.screen_width - renpy.image_size(dynamicimage)[0] ) / 2
            elif self.__step == 1:
                return 0
            else:
                raise StopIteration

image dynamicimage_victoryscene_composite = Composite(
    (1280,720),
    (0,0), "dynamicimage_victoryscene",
    Rpgfunction_getimagesize(), "dynamicimage_victoryscene",
    (0,0), "shaded.webp",
    Rpgfunction_getimagesize( True ), "dynamicimage_victoryscene"
    )
I don't guaranty my code at 100%, it's not my day, I'm exhausted and I don't have images to test it. But normally the code is correct and like it's now an iterable, Ren'py shouldn't complain.
 

Epadder

Programmer
Game Developer
Oct 25, 2016
568
1,058
No problems @anne O'nymous thanks for all the help :), unfortunately the original problem comes up again now. All the variables and values used to build the path for 'dynamicimage' to feed into renpy.image_size(dyanamicimage) are now triggering the original problem that they haven't been defined yet.

I think at this point it is less effort on both our parts just to move on, I can correct the original problem at the source by just fixing the images that aren't at the correct width.

I just wanted to create a clever way to fix it automatically :whistle:.

The error for reference:
Code:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/special-images.rpy", line 77, in script
    image dynamicimage_victoryscene_composite = Composite(
  File "game/special-images.rpy", line 82, in <module>
    rpgfunction_GetSizeForComposite( True ), "dynamicimage_victoryscene"
  File "game/customfunctions.rpy", line 1241, in next
    dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
NameError: global name 'rpgstat_EnemyRandomType' is not defined

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\bootstrap.py", line 313, in bootstrap
    renpy.main.main()
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\main.py", line 423, in main
    renpy.game.context().run(node)
  File "game/special-images.rpy", line 77, in script
    image dynamicimage_victoryscene_composite = Composite(
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\ast.py", line 980, in execute
    img = renpy.python.py_eval_bytecode(self.code.bytecode)
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\python.py", line 1937, in py_eval_bytecode
    return eval(bytecode, globals, locals)
  File "game/special-images.rpy", line 82, in <module>
    rpgfunction_GetSizeForComposite( True ), "dynamicimage_victoryscene"
  File "D:\Program Files\renpy-6.99.14.1-sdk\renpy\display\layout.py", line 302, in Composite
    xpos, ypos = pos
  File "game/customfunctions.rpy", line 1241, in next
    dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
NameError: global name 'rpgstat_EnemyRandomType' is not defined

Windows-8-6.2.9200
Ren'Py 7.1.3.1092
Adam and Gaia : THXE (Turbo Hyper Extreme Edition) Foundation
Fri Dec 14 15:42:34 2018
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,294
15,156
Code:
  File "game/customfunctions.rpy", line 1241, in next
    dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
NameError: global name 'rpgstat_EnemyRandomType' is not defined
There's time like this one, where I really hate Ren'py's prediction ; at least I assume that it's because it try to predict the image once that it happen.
In the code I gave above, try to change next like this :
Code:
        def next( self ):   
            self.__step += 1
            if self.__step == 0:
# The only change is the addition of the following line.
                if hasattr( store,  "dynamicimage_currentstep" ) is False: return 0
                dynamicimage = "battlechars/%s/%s_%s/%s_%s %s.webp" % (rpgstat_EnemyRandomType.capitalize(),rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,rpgstat_EnemyRandomType.capitalize(),dynamicimage_victory,dynamicimage_currentstep)
                return config.screen_width - renpy.image_size(dynamicimage)[0] if isDivided is False else ( config.screen_width - renpy.image_size(dynamicimage)[0] ) / 2
            elif self.__step == 1:
                return 0
            else:
                raise StopIteration
It will return a default value when dynamicimage_currentstep isn't defined yet ; so be careful, from all the variable used here, it must be the last that you define.
This should prevent Ren'py to complain before you effectively gave a value to the used variables. It will not be a problem since at this time Ren'py just predict the image, it don't try to effectively display it.
 

Epadder

Programmer
Game Developer
Oct 25, 2016
568
1,058
That does remove the crashing problem, but I think that part of the Composite never updates once it has been predicted.
I get this now:
You don't have permission to view the spoiler content. Log in or register now.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,294
15,156
but I think that part of the Composite never updates once it has been predicted.
I did some tests, then some searches, and Composite/LiveComposite don't "live" the image. It just compose it, and return something definitive. The screen update isn't due to Composite/LiveComposite, but to the displayable used to compose the image.
In other words, you can't dynamically change the position, whatever you try, while using Composite/LiveComposite.

The solution of your problem is somewhere else. Either by creating a , or by using , providing it with Composite/LiveComposite displayable.
The second one is probably the easiest if you don't have much knowledge in Ren'py/Python. It would look more or less like this :
Code:
image dynamicimage_victoryscene = "battlechars/[rpgstat_EnemyRandomType]/[rpgstat_EnemyRandomType]_[dynamicimage_victory]/[rpgstat_EnemyRandomType]_[dynamicimage_victory] [dynamicimage_currentstep].webp"
image dynamicimage_victoryscene_composite = ConditionSwitch(
    "rpgstat_EnemyRandomType == 'something'", Composite(
        (1280,720),
        (0,0), "dynamicimage_victoryscene",
        (1000 ,0), "dynamicimage_victoryscene",
        (0,0), "shaded.webp",
        ((1140,0), "dynamicimage_victoryscene"
        ),
    "rpgstat_EnemyRandomType == 'another thing'", Composite(
        (1280,720),
        (0,0), "dynamicimage_victoryscene",
        (1000 ,0), "dynamicimage_victoryscene",
        (0,0), "shaded.webp",
        ((1140,0), "dynamicimage_victoryscene"
        ),
    "True",  Composite(
        (1280,720),
        (0,0), "dynamicimage_victoryscene",
        (1000 ,0), "dynamicimage_victoryscene",
        (0,0), "shaded.webp",
        ((1140,0), "dynamicimage_victoryscene"
        ))
Note: I put the Composite directly in the ConditionSwitch code, but you can define them outside :
Code:
image EnemyRandomType_something = Composite(
        (1280,720),
        (0,0), "dynamicimage_victoryscene",
        (1000 ,0), "dynamicimage_victoryscene",
        (0,0), "shaded.webp",
        ((1140,0), "dynamicimage_victoryscene"
        )
image EnemyRandomType_another_thing = Composite(
        (1280,720),
        (0,0), "dynamicimage_victoryscene",
        (1000 ,0), "dynamicimage_victoryscene",
        (0,0), "shaded.webp",
        ((1140,0), "dynamicimage_victoryscene"
        )
image EnemyRandomType_default = Composite(
        (1280,720),
        (0,0), "dynamicimage_victoryscene",
        (1000 ,0), "dynamicimage_victoryscene",
        (0,0), "shaded.webp",
        ((1140,0), "dynamicimage_victoryscene"
        )

image dynamicimage_victoryscene_composite = ConditionSwitch(
    "rpgstat_EnemyRandomType == 'something'", EnemyRandomType_something,
    "rpgstat_EnemyRandomType == 'another thing'", EnemyRandomType_another_thing,
    "True",  EnemyRandomType_default )