Enables you to create code for Overwatch Workshop in Typescript.
For instance,
import { Native } from "./library/native";
import { Rule } from "./library/rule";
import { Vector } from "./library/vector";
import { Message } from "./library/Message";
import { Players, Button } from "./library/Player";
var textPosition: Vector;
var totalShots = 0;
Native.rule("Initialize text",
Rule.Events.Ongoing.global,
true,
() => {
textPosition = Vector.zero;
Message.InWorld.createText(Players.all, totalShots.toString(), textPosition, 3, Message.InWorld.Clipping.AlwaysVisible);
}
);
Native.rule("Move text to shot target",
Rule.Events.Ongoing.eachPlayer(Rule.Events.Team.All, Rule.Events.Players.All),
Rule.Event.player.isHoldingButton(Button.PrimaryFire),
() => {
totalShots++;
textPosition = Vector.getFirstIntersectionBetween(
Rule.Event.player.position + Vector.up,
Rule.Event.player.position + 100 * Rule.Event.player.facingDirection + Vector.up
);
}
);
Compiles into
rule("Initialize global variables")
{
event
{
Ongoing - Global;
}
actions
{
Set Global Variable(B, 0);
}
}
rule("Initialize text")
{
event
{
Ongoing - Global;
}
actions
{
Set Global Variable(A, Vector(0, 0, 0));
Create in-world text(All Players(Team(All)), Global Variable(B), Global Variable(A), 3, Do not clip, Visible to position and string);
}
}
rule("Move text to shot target")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Button Held(Event Player, Primary Fire) == true;
}
actions
{
Set Global Variable(B, Add(Global Variable(B), 1));
Set Global Variable(A, Ray cast hit position(Add(Position of(Event Player), Up), Add(Add(Position of(Event Player), Multiply(100, Facing direction of(Event Player))), Up), All Players(Team(All)), Event Player, true));
}
}
which can then be copy-pasted into Overwatch.
https://github.com/Beier/Omnium/releases
Have a look at the examples in my test cases. https://github.com/Beier/Omnium/tree/master/Omnium.Tests/ToWorkshop/Data
I have created a library of modules, classes and functions that can be used to call all methods in Overwatch workshop. This library is bundled with the release. Most of the library just consist of normal TypeScript classes that you can edit as you see fit, but some of the classes and modules have special meaning to the compiler.
If you are looking for a specific action/function from the Workshop, make a string search for it in the library code.
To create a rule, import Native and Rule, and call the method Native.rule
import { Native } from "./library/native";
import { Rule } from "./library/rule";
Native.rule(
"My rule",
Rule.Events.Ongoing.eachPlayer(),
Rule.Event.player.isAlive,//Condition can be omitted. Combine multiple conditions with && and ||
() => {
//Actions
}
);
All event types are found under Rule.Events
.
Variables related to the rule, such as Event Player are found under Rule.Event
.
Variables are declared as one typically would do it in TypeScript. Global variables (declared outside methods) will just be translated into a overwatch global variable. If you declare a local variables or parameters, the compilier will attempt to merge them if their usages does not overlap, and then transform them into global variables. Local variables not spanning wait statements might be used by multiple rules.
Player variables are declared by enheriting from Native.PlayerVars
.
class PlayerVars extends Native.PlayerVars {
public firstShotPosition: Vector = null;
public shots = 0;
public totalDistance = 0;
public icon : Icon.Icon = null;
}
And accessed through ex. Rule.Event.player.vars<PlayerVars>().firstShotPosition
.
Note that I am not super happy with this implementation, and might change it to something like public firstShotPosition : PlayerVar<number>
, firstShotPosition.getFor(Rule.Event.player)
. That would work better with state in classes, which I would like to add in the future.
All functions in the Native module have special meaning to the compiler.
The native module has functions like callNativeArg1Function
or callNativeArg10Action
, which is how an action or function in the native overwatch code is invoked. You should not need to call this directly, as all the actions are already part of the library.
For instance, the Team class in the library contain the code
export class Team {
public static get team1() {
return Native.callNativeArg1Function<TeamConst, Team>("Team", true, false, TeamConst.Team1);
}
public static get team2() {
return Native.callNativeArg1Function<TeamConst, Team>("Team", true, false, TeamConst.Team2);
}
public get score() {
return Native.callNativeArg1Function<Team, number>("Team score", true, false, this);
}
public set score(value: number) {
Native.callNativeArg2Action<Team, number>("Set team score", false, true, this, value);
}
...
}
Which means you can call Team.team1.score
instead of Native.callNativeArg1Function<Team, number>("Team score", true, false, Native.callNativeArg1Function<TeamConst, Team>("Team", true, false, TeamConst.Team1)
.
However, if the Overwatch team adds a new action to the Workshop, it will not be in the library, but you can still call it with theese methods. Also, if you see this, please notify me, or make a pull request on the library code here on GitHub.
You can make strings, like you would normally in typescript.
Message.send(Players.all, Rule.Event.player + " failed!");
The compiler will check that the string can be constructed based on the strings registered in library/strings.ts, and convert this into
String("{0}!", String("{0} {1}", Event Player, String("failed", Null, Null, Null), Null), Null, Null)
If you try to make a string that can not be constructed, like Message.send(Players.all, Rule.Event.player + " died!");
, the compilation will fail.
If the compiler is missing a string that exists in overwatch, you can register it with Native.registerString("...");
. Please also notify me, or make a pull request on the library code here on GitHub.
Normal typescript arrays are not supported, since operations like slice and splice work differently in Overwatch than in normal JavaScript. Instead, use the generic list class
import { Native } from "./library/native";
import { List } from "./library/List";
Native.rule("Test list",
Rule.Events.Ongoing.global,
() => {
var list = List.empty<number>();
list = list.append(3);
list = list.append(1);
list = list.append(2);
list = list.sorted;
}
);
Many library functions such as Players.all
also return lists.
The compiler supports for, for-of and while loops. If you write a for loop with a constant number of iterations, such as
var list : List<number> = List.empty<number>();
for (var i = 0; i < 10; i++) {
list = list.append(i);
}
The compiler will unroll it to
var list : List<number> = List.empty<number>();
list = list.append(0);
list = list.append(1);
list = list.append(2);
list = list.append(3);
list = list.append(4);
list = list.append(5);
list = list.append(6);
list = list.append(7);
list = list.append(8);
list = list.append(9);
If the number of iterations is unknown, the compiler will instead use the loop
action, but that requires a wait
somewhere in the loop. For instance
import { Native, wait } from "./library/native";
import { Rule } from "./library/rule";
import { Message } from "./library/message";
import { Players } from "./library/player";
import { List } from "./library/list";
Native.rule("Test max array size",
Rule.Events.Ongoing.global,
() => {
var list = List.empty<number>();
while (true) {
for (var i = 0; i < 10; i++) {
list = list.append(list.length);
}
Message.send(Players.all, list.length.toString());
wait(0.3);
}
}
);
Compiles to
rule("Initialize global variables")
{
event
{
Ongoing - Global;
}
actions
{
Set Global Variable(A, 0);
}
}
rule("Test max array size")
{
event
{
Ongoing - Global;
}
actions
{
Skip If(Compare(Global Variable(A), ==, 1), 2);
Set Global Variable(B, Empty Array);
Set Global Variable(A, 1);
Set Global Variable(C, Append To Array(Global Variable(B), Count Of(Global Variable(B))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(C, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Set Global Variable(B, Append To Array(Global Variable(C), Count Of(Global Variable(C))));
Big Message(All Players(Team(All)), Count Of(Global Variable(B)));
Wait(0.3, Ignore condition);
Loop;
}
}