overpy 9.6.3 → 9.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/overpy.js +49 -26
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -596,7 +596,7 @@ Suppresses the specified warnings globally across the program. Warnings must be
596
596
 
597
597
  ## #!allowMacroRedeclaration
598
598
 
599
- If specified, will replace the existing macro instead of throwing an error. Can be useful for OOP-like projects where the same codebase is used for multiple different gamemodes.
599
+ If specified, allows redefining a `macro`, `#!define` or `enum` member. Can be useful for OOP-like projects where the same codebase is used for multiple different gamemodes.
600
600
 
601
601
  ## #!debugElementCount
602
602
 
@@ -616,7 +616,7 @@ You can check [here](https://github.com/Zezombye/overpy/issues/33) for a list of
616
616
 
617
617
  The `#!disableOptimizations` directive can be used to disable all optimizations done by the compiler. Should be only used for debugging, if you suspect that OverPy has bugs in its optimizations, or if you want to generate an unoptimized instruction for some reason.
618
618
 
619
- The `#!optimizeForSize` directive prioritizes lowering the number of elements over optimizing the runtime (see [here](https://github.com/Zezombye/overpy/issues/238) for a list of optimizations).
619
+ The `#!optimizeForSize` directive prioritizes lowering the number of elements over optimizing the runtime (see [here](https://github.com/Zezombye/overpy/issues/238) for a list of optimizations). You can use `#!optimizeForSizeAggressive` (applied codebase-wide and MUST be used alongside `#!optimizeForSize`) for even more optimizations if you really need to squeeze every last element, at the cost of readability.
620
620
 
621
621
  The `#!optimizeStrict` directive disables some optimizations that may cause issues in extreme cases of type conversion. For example:
622
622
 
@@ -956,6 +956,8 @@ OverPy will store the array in a string and automatically decompress it, which t
956
956
 
957
957
  For more control over the compression (eg if you have separate arrays to compress), you can use the `compress` and `decompressNumbers`/`decompressVectors` functions.
958
958
 
959
+ You can also use the `#!useVariableForCompressionAlphabet` compiler option to use a global variable `__compressionAlphabet__` instead of a hardcoded string for the alphabet, which will save further elements.
960
+
959
961
  ## splitDictArray/tabular
960
962
 
961
963
  `splitDictArray` maps an array of dictionaries to variables. For example:
package/overpy.js CHANGED
@@ -38600,6 +38600,10 @@ ${scriptText}`, {
38600
38600
  setPlayervarInitRuleName(unescapeString(ruleName, true));
38601
38601
  return;
38602
38602
  }
38603
+ if (content2.startsWith("#!optimizeForSizeAggressive")) {
38604
+ setOptimizationForSizeAggressive(true);
38605
+ return;
38606
+ }
38603
38607
  if (content2.startsWith("#!optimizeForSize")) {
38604
38608
  addToken("__enableOptimizeForSize__");
38605
38609
  return;
@@ -45187,7 +45191,7 @@ astParsingFunctions.__if__ = function(content) {
45187
45191
  }
45188
45192
  }
45189
45193
  }
45190
- if (optimizeForSize2 && (content.args[0].name === "__not__" || content.children.length === 1 && includeEnd && ["__equals__", "__inequals__", "__greaterThan__", "__greaterThanOrEquals__", "__lessThan__", "__lessThanOrEquals__"].includes(content.args[0].name))) {
45194
+ if (optimizeForSize2 && optimizeForSizeAggressive && (content.args[0].name === "__not__" || content.children.length === 1 && includeEnd && ["__equals__", "__inequals__", "__greaterThan__", "__greaterThanOrEquals__", "__lessThan__", "__lessThanOrEquals__"].includes(content.args[0].name))) {
45191
45195
  let label = "__label_if_" + getUniqueNumber() + "__";
45192
45196
  content.parent.children.splice(content.parent.childIndex + 1, 0, ...content.children, new Ast2(label, [], [], "Label"), getAstForUselessInstruction());
45193
45197
  if (content.args[0].name === "__not__") {
@@ -48011,9 +48015,6 @@ function parseAstRules(rules) {
48011
48015
  }
48012
48016
  rulesResult.push(parseAst(rule));
48013
48017
  }
48014
- setOptimizationEnabled2(true);
48015
- setOptimizeStrict2(false);
48016
- setOptimizationForSize2(false);
48017
48018
  return rulesResult;
48018
48019
  }
48019
48020
  function parseAst(content) {
@@ -64661,9 +64662,6 @@ function astRulesToWs(rules) {
64661
64662
  ruleElementCounts.sort((a, b) => b.elements - a.elements);
64662
64663
  elementCountSummary = "/* Element count: (total " + nbElements + ")\n\n" + ruleElementCounts.map((r) => ("" + r.elements).padStart(5, " ") + ": rule " + escapeString(r.name, false) + (r.file ? " (" + r.file + ")" : "")).join("\n") + "\n\n*/\n\n";
64663
64664
  }
64664
- setOptimizationEnabled2(true);
64665
- setOptimizeStrict2(false);
64666
- setOptimizationForSize2(false);
64667
64665
  return { compiledRules, elementCountSummary };
64668
64666
  }
64669
64667
  function astRuleConditionToWs(condition) {
@@ -64989,19 +64987,32 @@ function astToWs(content) {
64989
64987
  if (content.args.length === 0) {
64990
64988
  incrementNbElements();
64991
64989
  return tows("__emptyArray__", valueKw);
64992
- } else if (optimizeForSize2 && content.parent?.name !== "createWorkshopSettingEnum" && content.args.every((x) => x.name === "__customString__" && x.args.length === 1)) {
64993
- let separator = "\uEC51";
64994
- let separatorAst = getAstForCustomString(separator);
64995
- let str = content.args.map((x) => x.args[0].name).join(separator);
64996
- if (!str.includes("0")) {
64997
- separator = "0";
64998
- separatorAst = new Ast2("__firstOf__", [getAstForNull()]);
64999
- } else if (!str.includes("(1.00, 0.00, 0.00)") && getUtf8Length(str) % (STR_MAX_LENGTH - "{0}".length) + content.args.length * ("(1.00, 0.00, 0.00)".length - "\uEC51".length) <= STR_MAX_LENGTH) {
65000
- separator = "(1.00, 0.00, 0.00)";
65001
- separatorAst = new Ast2("__firstOf__", [new Ast2("Vector.LEFT")]);
65002
- }
65003
- str = str.replaceAll("\uEC51", separator);
65004
- return astToWs(new Ast2(".split", [getAstForCustomString(str), separatorAst]));
64990
+ } else if (optimizeForSize2 && optimizeForSizeAggressive && content.parent?.name !== "createWorkshopSettingEnum") {
64991
+ if (content.args.every((x) => x.name === "__customString__" && x.args.length === 1)) {
64992
+ let separator = "\uEC51";
64993
+ let separatorAst = getAstForCustomString(separator);
64994
+ let str = content.args.map((x) => x.args[0].name).join(separator);
64995
+ if (!str.includes("0")) {
64996
+ separator = "0";
64997
+ separatorAst = new Ast2("__firstOf__", [getAstForNull()]);
64998
+ } else if (!str.includes("(1.00, 0.00, 0.00)") && getUtf8Length(str) % (STR_MAX_LENGTH - "{0}".length) + content.args.length * ("(1.00, 0.00, 0.00)".length - "\uEC51".length) <= STR_MAX_LENGTH) {
64999
+ separator = "(1.00, 0.00, 0.00)";
65000
+ separatorAst = new Ast2("__firstOf__", [new Ast2("Vector.LEFT")]);
65001
+ }
65002
+ str = str.replaceAll("\uEC51", separator);
65003
+ return astToWs(new Ast2(".split", [getAstForCustomString(str), separatorAst]));
65004
+ } else if (content.args.length >= 3 && content.args.every((x) => areAstsAlwaysEqual(x, content.args[0]))) {
65005
+ let estimatedElementCount = 1 + (content.args[0].name === "__customString__" ? 4 : content.args[0].args.length);
65006
+ if (estimatedElementCount === 1 && content.args.length >= 11 || estimatedElementCount === 2 && content.args.length >= 5 || estimatedElementCount === 3 && content.args.length >= 4 || estimatedElementCount >= 4) {
65007
+ return astToWs(new Ast2("__mappedArray__", [
65008
+ new Ast2(".split", [
65009
+ getAstForCustomString("0".repeat(content.args.length - 1)),
65010
+ new Ast2("__firstOf__", [getAstForNull()])
65011
+ ]),
65012
+ content.args[0]
65013
+ ]));
65014
+ }
65015
+ }
65005
65016
  }
65006
65017
  } else if (content.name === "chaseAtRate" || content.name === "chaseOverTime") {
65007
65018
  var newName = content.name === "chaseAtRate" ? "AtRate__" : "OverTime__";
@@ -65821,7 +65832,13 @@ function compileRules(astRules) {
65821
65832
  if (DEBUG_MODE) {
65822
65833
  console.log(parsedAstRules);
65823
65834
  }
65835
+ setOptimizationEnabled2(true);
65836
+ setOptimizeStrict2(false);
65837
+ setOptimizationForSize2(false);
65824
65838
  var { compiledRules, elementCountSummary } = astRulesToWs(parsedAstRules);
65839
+ setOptimizationEnabled2(true);
65840
+ setOptimizeStrict2(false);
65841
+ setOptimizationForSize2(false);
65825
65842
  setFileStack(getInternalFileStack());
65826
65843
  var result = elementCountSummary + compiledCustomGameSettings;
65827
65844
  if (!excludeVariablesInCompilation) {
@@ -66650,7 +66667,7 @@ function parseLines(lines) {
66650
66667
  }
66651
66668
  }
66652
66669
  let enumMemberName = childrenLines[k].tokens[0].toString();
66653
- if (enumMemberName in enumMembers[args[0].name]) {
66670
+ if (enumMemberName in enumMembers[args[0].name] && !allowMacroRedeclaration) {
66654
66671
  error("Duplicate enum member '" + args[0].name + "." + enumMemberName + "''");
66655
66672
  }
66656
66673
  enumMembers[args[0].name][enumMemberName] = enumValue;
@@ -69669,6 +69686,8 @@ var enableOptimization;
69669
69686
  var setOptimizationEnabled2 = (enabled) => enableOptimization = enabled;
69670
69687
  var optimizeForSize2;
69671
69688
  var setOptimizationForSize2 = (size) => optimizeForSize2 = size;
69689
+ var optimizeForSizeAggressive;
69690
+ var setOptimizationForSizeAggressive = (aggressive) => optimizeForSizeAggressive = aggressive;
69672
69691
  var optimizeStrict;
69673
69692
  var setOptimizeStrict2 = (strict) => optimizeStrict = strict;
69674
69693
  var macros = [];
@@ -69799,6 +69818,7 @@ function resetGlobalVariables(language) {
69799
69818
  compiledCustomGameSettings = "";
69800
69819
  enableOptimization = true;
69801
69820
  optimizeForSize2 = false;
69821
+ optimizeForSizeAggressive = false;
69802
69822
  optimizeStrict = false;
69803
69823
  uniqueNumber = 1;
69804
69824
  globalInitDirectives = [];
@@ -72116,10 +72136,10 @@ var key;
72116
72136
  // src/data/opy/preprocessing.ts
72117
72137
  var preprocessingDirectives = {
72118
72138
  "allowMacroRedeclaration": {
72119
- "description": "If enabled, redefining a `macro` or `#!define` will not throw an error but will overwrite the previous definition. Can be useful for OOP-like projects where the same codebase is used for multiple different gamemodes."
72139
+ "description": "If enabled, redefining a `macro`, `#!define` or `enum` member will not throw an error but will overwrite the previous definition. Can be useful for OOP-like projects where the same codebase is used for multiple different gamemodes."
72120
72140
  },
72121
72141
  "define": {
72122
- "description": '**Warning**: This directive performs a text-based replacement! Use `macro` or `const` instead, unless absolutely necessary.\n\nCreates a macro, like in C/C++. Macros must be defined before any code. Examples:\n\n #!define currentSectionWalls A\n #!define GAME_NOT_STARTED 3`\n\nFunction macros are supported as well:\n\n #!define getFirstAvailableMei() [player for player in getPlayers(Team.2) if not player.isFighting][0]\n #!define spawnMei(type, location) getFirstAvailableMei().meiType = type\\\n wait(0.1)\\\n getFirstAvailableMei().teleport(location)\\\n getFirstAvailableMei().isFighting = true\n\nNote the usage of the backslashed lines.\n\nJS scripts can be inserted with the special `__script__` function:\n\n #!define addFive(x) __script__("addfive.js")\n\nwhere the `addfive.js` script contains `x+5` (no `return`).\n\nArguments of JS scripts are inserted automatically at the beginning (so `addFive(123)` would cause `var x = 123;` to be inserted). The script is then evaluated using `eval()`.\n\nA `vect()` function is also inserted, so that `vect(1,2,3)` returns an object with the correct properties and `toString()` function.\n\nWhen resolving the macro, the indentation on the macro call is prepended to each line of the replacement.\n',
72142
+ "description": '**Warning**: This directive performs a text-based replacement! Use `macro` instead, unless absolutely necessary.\n\nCreates a macro, like in C/C++. Macros must be defined before any code. Examples:\n\n #!define currentSectionWalls A\n #!define GAME_NOT_STARTED 3`\n\nFunction macros are supported as well:\n\n #!define getFirstAvailableMei() [player for player in getPlayers(Team.2) if not player.isFighting][0]\n #!define spawnMei(type, location) getFirstAvailableMei().meiType = type\\\n wait(0.1)\\\n getFirstAvailableMei().teleport(location)\\\n getFirstAvailableMei().isFighting = true\n\nNote the usage of the backslashed lines.\n\nJS scripts can be inserted with the special `__script__` function:\n\n #!define addFive(x) __script__("addfive.js")\n\nwhere the `addfive.js` script contains `x+5` (no `return`).\n\nArguments of JS scripts are inserted automatically at the beginning (so `addFive(123)` would cause `var x = 123;` to be inserted). The script is then evaluated using `eval()`.\n\nA `vect()` function is also inserted, so that `vect(1,2,3)` returns an object with the correct properties and `toString()` function.\n\nWhen resolving the macro, the indentation on the macro call is prepended to each line of the replacement.\n',
72123
72143
  "snippet": "define $0"
72124
72144
  },
72125
72145
  "debugElementCount": {
@@ -72147,13 +72167,16 @@ var preprocessingDirectives = {
72147
72167
  "description": "Add a rule to obtain an unsanitized '<' character which can be used to create <tx> and <fg> tags.\n\n**WARNING**: The inserted rule creates a dummy bot then immediately destroys it. This has the side effect of triggering each-player rules and may break your gamemode (though if properly coded, it shouldn't).\n\nThe `__holygrail__` variable can be used to obtain the raw '<' character, although it is not necessary as OverPy will automatically take care of the conversion, meaning you can put raw texture tags in strings.\n\nFor color, use the <fgRRGGBBAA> tag, where RR/GG/BB are the hex color value, and AA is the hex transparency value (00 = transparent, FF = opaque).\nExample: `print('<fgFF0000FF>Red text</fg>')`.\n\nFor textures, use the <TX> standalone tag, with the texture id as seen in https://workshop.codes/wiki/articles/tx-reference-sheet.\nExample: `print('<TXC0000000002DD21>')` will display the mouse cursor texture.\n\nAdditionally, you can use the `Texture` enum (such as `Texture.MOUSE_CURSOR`), and OverPy will automatically optimize it.\n\nOverPy will also replace `'<tx1234>'` to the correct full texture id, but only if the entire tag is inside a string (`'<tx{}>'.format(id)` will not work, but `'<tx{}>'.format(1234)` will)."
72148
72168
  },
72149
72169
  "disableOptimizations": {
72150
- "description": "Disables all optimizations done by the compiler for the current block or file, up until the end of the block/file or the next `#!enableOptimizations` directive."
72170
+ "description": "Disables all optimizations done by the compiler for the current block, up until the end of the block or the next `#!enableOptimizations` directive."
72151
72171
  },
72152
72172
  "enableOptimizations": {
72153
72173
  "description": "Re-enables optimizations after a `#!disableOptimizations` directive. If no `#!disableOptimizations` directive was encountered, this directive does nothing."
72154
72174
  },
72155
72175
  "optimizeForSize": {
72156
- "description": "Prioritizes lowering the number of elements over optimizing the runtime. Effective for the current block or file, up until the end of the block/file or the next `#!disableOptimizeForSize` directive."
72176
+ "description": "Prioritizes lowering the number of elements over optimizing the runtime. Effective for the current block, up until the end of the block or the next `#!disableOptimizeForSize` directive."
72177
+ },
72178
+ "optimizeForSizeAggressive": {
72179
+ "description": "Enables aggressive optimizations for size, which may significantly lower the readability of the code (for now, replacing `if` by `skip if` in some cases and automatically compressing arrays).\n\n**NOTE:** This directive is applied for the whole codebase and MUST be used alongside `#!optimizeForSize` for it to have an effect."
72157
72180
  },
72158
72181
  "disableOptimizeForSize": {
72159
72182
  "description": "Re-enables optimizations after a `#!optimizeForSize` directive. If no `#!optimizeForSize` directive was encountered, this directive does nothing."
@@ -72169,7 +72192,7 @@ Those optimizations (and others) will be disabled so that the behavior of the ga
72169
72192
 
72170
72193
  This directive is added by default upon decompilation. Only remove it if you are sure that your gamemode does not rely on type conversion tricks. It is recommended to use a website such as http://diffchecker.com to compare the differences in the output when enabling/disabling this directive.
72171
72194
 
72172
- This directive is effective for the current block or file, up until the end of the block/file or the next \`#!disableOptimizeStrict\` directive.`
72195
+ This directive is effective for the current block, up until the end of the block or the next \`#!disableOptimizeStrict\` directive.`
72173
72196
  },
72174
72197
  "disableOptimizeStrict": {
72175
72198
  "description": "Re-enables optimizations after a `#!optimizeStrict` directive. If no `#!optimizeStrict` directive was encountered, this directive does nothing."
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/Zezombye/overpy"
8
8
  },
9
9
  "description": "High-level language for the Overwatch Workshop, with decompilation and compilation.",
10
- "version": "9.6.3",
10
+ "version": "9.6.4",
11
11
  "readme": "README.md",
12
12
  "keywords": [
13
13
  "overpy",