OverPy, a high-level language for the workshop

OverPy is a high-level language for the workshop: a custom built language that compiles to the workshop language.

OverPy allows you to manage your gamemodes much more easily, thanks to its ability to code with modern development practices: multiple files, switches, dictionaries, macros, function macros, enums, built-in JS preprocessing…

It contains built-in optimizations, allowing you to focus on making the source code clearer.

OverPy also includes a decompiler, to not have to recode your entire gamemode, and to get started as fast as possible.

All in-game languages are supported, meaning you do not need to switch your in-game language to English to use it.

The VS Code extension includes syntax highlighting, autocompletion, and documentation.

Big thanks to arxenix who parsed the workshop documentation: https://github.com/arxenix/owws-documentation/blob/master/workshop.json

As well as the Overtool team for allowing me to datamine translations from the game.

Installation

Please refer to the wiki: https://github.com/Zezombye/overpy/wiki/General-usage

Optimization

Even if you prefer the workshop UI to coding in a text file, OverPy can serve as a tool to save elements and improve run time.

It can be done very easily by decompiling then compiling (OverPy guarantees that your gamemode will have the same effective behavior).

Some examples of saved elements:

Loot Quest - Regular Maps (4QV99) by Delwion : 19998 -> 18750 (-1248)
OverHammer (KYGBP) by Kinkku : 19865 -> 17664 (-2201)
Among Us (6C777) by Momaker: 19909 -> 17575 (-2334)

Preprocessing

Thanks to macros, you can declare constants and additional variables.

As such, instead of using “magic numbers”, you can do :

#!define PLAYER_SPEED 5.5

This will replace PLAYER_SPEED with 5.5, and save a variable slot while still retaining comprehension.

To declare an enum, you can use the enum keyword:

enum GameStatus:
    GAME_NOT_STARTED = 1,
    GAME_IN_PROGRESS,
    GAME_FINISHED

If you find yourself running out of variables, you can replace some of them by an array lookup much more easily:

#!define somevar array[3]

Moreover, inline functions allow you to use complex values while not duplicating the code:

#!define knockbackMult 1.3
#!define knockbackScalar 0.2
#!define kbMultScalar (knockbackMult * (1-knockbackScalar))
#!define kbRatioMult (knockbackMult * knockbackScalar)

Another example is for color reevaluation, to display an HUD Text with the color in a variable. Here, I use a macro to dynamically replace color with the color I specify, and minimise the code duplication instead of copy-pasting the whole hudHeader function 4 times.

enum ColorVar:
    BLUE,
    RED,
    YELLOW,
    ORANGE
 
globalvar currentColor = ColorVar.BLUE
 
#!define coloredText(str, color) hudHeader(getAllPlayers() if currentColor == ColorVar.(color) else [], str, HudPosition.LEFT, 0, Color.(color), HudReeval.VISIBILITY_SORT_ORDER_AND_STRING)
 
rule "Initialize the colored texts":
    coloredText("some text", BLUE)
    coloredText("some text", RED)
    coloredText("some text", YELLOW)
    coloredText("some text", ORANGE)

JavaScript macros

To give even more power to preprocessing, OverPy allows you to use JavaScript macros. This allows for example an easy way to loop the creation of rules:

var result = "";
for (var i = 1; i <= 6; i++) {
    result += `
rule "destroy barricade ${i} if hp is 0":
     @Event global
     @Condition barricade${i}hp <= 0
     barricade${i} = vect(0,-1000,0)`
}
result;

Or even to declare your gamemode data in dictionaries, where the JS macro will iterate on the dictionary and populate the arrays:

var result = "";
 
result += "playerSpawns = ["+zoneData.map(x => x.playerSpawn).join(", ")+"]\n";
result += "zonesUnlockLocation = ["+zoneData.map(x => x.unlockLocation).join(", ")+"]\n";
result += "zonesUnlockMoney = ["+zoneData.map(x => x.unlockMoney).join(", ")+"]\n";
for (var i = 0; i < zoneData.length; i++) {
    result += "meiSpawns["+i+"] = ["+zoneData[i].meiSpawns.join(", ")+"]\n";
}
result;
generateZoneVariables([{
        playerSpawn: vect(-42.63, 17.03, 83.12),
        meiSpawns: [
            vect(-22.44, 28, 51.66),
            vect(-45.60, 11, 48.22),
        ],
        unlockLocation: vect(-57.10, 16.2, 56.74),
        unlockMoney: 500,
    },{
        playerSpawn: vect(-48.86, 19, 51.64),
        meiSpawns: [
            vect(-73.12, 13, 35.11),
        ],
        unlockLocation: vect(-53.81, 10.87, 36.71),
        unlockMoney: 700,
    },
])

Switches

To replace long if-elif-else chains that test the same variable for a specific value, OverPy allows you to use switches:

switch eventPlayer.enemyHero:
    case Hero.ANA:
        eventPlayer.resistance = 2
        break
    case Hero.ASHE:
        eventPlayer.resistance = 3
        break
    default:
        eventPlayer.resistance = 4

This allows for both a size and runtime optimization, as the value of eventPlayer.enemyHero is not recalculated each time.

Inline dictionaries

The above switch can be replaced by an inline dictionary, as the same action is ran every time:

eventPlayer.resistance = {
    0: 4,
    Hero.ANA: 2,
    Hero.ASHE: 3,
}[eventPlayer.enemyHero]

Quality of life

Automatic string splitting: you can use strings with more than 128 chars, and more than 3 formatters.
Workshop settings sort: you can specify a sort order for workshop settings.
Built-in math functions: log(), lineIntersectsSphere(), math.pi, math.e

Help & Feedback

Want to know more? Join the discord for help and feedback: https://discord.gg/EEMjjFB

10 Likes

Really interesting, i assume multidimensional arrays and high order functionialities like Lambda is also invented and useable from the Python perspective for our advantages, lastly would be an overkill. Nice done :slight_smile:

2 Likes

I don’t even know what to say, I just…

Yes.

1 Like

Thanks :slight_smile:

Multi-dimensional arrays are there, however I aim to keep OverPy relatively simple to understand, so for now you cannot put lambdas (you can however put function macros). The only place where a lambda is used is when sorting, but that is only because you can use Current Array Element.

2 Likes

Got it thank you :slight_smile:

OverPy v6.6.3:

  • All PTR actions and values are now supported
  • To compile for Live, select the latest 6.5.x version
  • It is possible to decompile a Live gamemode, then compile it again to export it to PTR (a very quick way of doing this is using the demo: https://zezombye.github.io/overpy/demo)

Optimizations and QoL stuff coming in the upcoming days :slight_smile:

2 Likes

You’re my hero. There’s just no way I was going to manually update 60+ rules.

So fast, good job^^.

Really cool! I remember seeing something like this for TypeScript as well. They leveraged the existing AST transformation tools for the JS language.

Looks like you made your own Python parser. I guess there aren’t many existing AST tools for Python as JS?

I really wish Overwatch’s language was something like this with the ability to connect VSCode to a windowed Overwatch instance. I almost never use the interface. Just a bunch of copy/paste from an editor.

Keep in mind OverPy isn’t Python; it just uses a Python-like syntax (no semicolons, blocks delimited by indentation, Python function names when possible such as len() or .format(), etc). Instead it is its own language, as such I had no other choice than making my own parser :slight_smile:

1 Like

All my disabled rules were deleted, and I need them for testing and organization, so I guess I’ll just wait until the ptr update makes it to the live servers… :sweat_smile:
Also, Wait(False) is a crime against humanity, and so is replacing my 0’s with nulls, even if they have the same behavior. I just don’t trust Workshop to parse this correctly.

Right, in the end OverPy optimizes the gamemodes… which includes the removal of unused code ^^

You can manually uncomment the rules and add the @Disabled annotation instead, but I understand this is annoying if you still wish to program in the UI.

For the 0 -> False/Null replacements, rest assured I have tested every single one of these to make sure they behave correctly. However I agree that this leads to reading confusion if you program in the UI, in an upcoming update they will only be applied with the #!optimizeForSize compiler option.

Trust me, this is good. Ever since I started using OverPy, using the UI has felt extremely sluggish. (I still use it for small tests, but for actual gamemodes, I’m team OverPy for life)

1 Like

I actually use OverPy and i recommend!

1 Like