• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

My little project - scripting in Typescript

Humberd

Żal Adzi
Joined
Nov 7, 2009
Messages
10
Reaction score
8
I always wanted to start writing my own OTS, but what repelled me for years was a dynamic nature of lua and a very poor IDE support.

For the last few days I've been working on bringing static typing and full IDE support to the forgotten server.
Now, I can write scripts in Typescript, which then are transpiled to lua scripts.
Here is an example of a script that instantly kills a target creature.

JavaScript:
const talkAction = new TalkAction(
    '!kill_target',
    '/kill_target',
    '!kt',
    '/kt',
);

talkAction.onSay((player, words, param) => {
  const target = player.getTarget();
  if (!target || !target.isCreature()) {
    player.sendTextMessage(
        MessageClasses.MESSAGE_STATUS_WARNING,
        'You must target a creature to use this command.',
    );
    return false;
  }
  target.addHealth(-target.getHealth());
  target.getPosition().sendMagicEffect(MagicEffectClasses.CONST_ME_MORTAREA);
  player.sendTextMessage(MessageClasses.MESSAGE_STATUS_CONSOLE_BLUE, `Killed ${target.getName()}`);
  return false;
});

talkAction.access(true);
talkAction.accountType(AccountType.ACCOUNT_TYPE_GOD);
talkAction.register();

export {};

Here is how it looks transpiled
Lua:
local ____exports = {}
local talkAction = TalkAction("!kill_target", "/kill_target", "!kt", "/kt")
talkAction:onSay(function(player, words, param)
    local target = player:getTarget()
    if not target or not target:isCreature() then
        player:sendTextMessage(MESSAGE_STATUS_WARNING, "You must target a creature to use this command.")
        return false
    end
    target:addHealth(-target:getHealth())
    target:getPosition():sendMagicEffect(CONST_ME_MORTAREA)
    player:sendTextMessage(
        MESSAGE_STATUS_CONSOLE_BLUE,
        "Killed " .. target:getName()
    )
    return false
end)
talkAction:access(true)
talkAction:accountType(ACCOUNT_TYPE_GOD)
talkAction:register()
return ____exports


Here is how it looks in my IDE, all the types are visible.
1705876334500.png

I get compilation errors and IDE highlights whenever I make some typing mistake.
In this screenshot I forgot to check if a target is null
1705876449646.png

I have a very nice autocomplete for enum types
1705876497889.png

I can also easily explore what each object has inside
1705876568880.png

What do you think?
 
This is cool! :) However I wonder regarding the implementation I hope this doesn't make you too much to repeat yourself describing all of this structure. I believe to achieve this MagicEffectClasses you have to always keep c++ enums and this LUA/TS enums together in-sync but if this is the only limitation it is still too much valuable to have a little hassle. :)
 
So currently it was all manually extracted from cpp files. All the objects, enums, methods, all by hand. It took about 10 hours with help of GitHub Copilot. Without him, it would take much much longer. With this approach the burden to keep the d.ts files would lie in hands of a commiter.

On the other hand I tried generating it from cpp AST, but the more I dug into it the more it turned out to be a very difficult task to do. After a day I gave up on this approach.

The feasible approach would be a runtime generation. Maybe a TFS flag, which when provided would generate d.ts files. But that would require writing some proxy code in the cpp code which defines Lua types
 
No one will like a solution where TFS has to integrate something in code. But as a github action job after every PR merge. Maybe? :) It is cool you invested your time in such a project.
Post automatically merged:

inb4 you use continue in a loop or switch statement 🤣
Did we did a post in the same second? :D :D Yeah, I wonder how it would compile the "continue" syntax too since lua doesn't have this :D But it could be easily implemented as such over TS side :)
 
No one will like a solution where TFS has to integrate something in code. But as a github action job after every PR merge. Maybe? It is cool you invested your time in such a project.:)
Post automatically merged:


Did we did a post in the same second? Yeah, I wonder how it would compile the "continue" syntax too since lua doesn't have this But it could be easily implemented as such over TS side :D:D:D:)


I honestly wouldn't say that typescript is a language, but rather a "mask", you write in typescript and then it is transpiled to the language in which it was implemented, in lua 5.2 it seems that 'continue' was implemented, lower versions, it should be some technique, like 'goto' for example.

In short, even php can support typescript, the question is, for what, since php supports typing/Classes.
 
Last edited:
Also make sure to +1 all the indexes in arrays!
I'm fairly sure Typescript does it behind the scenes.
Post automatically merged:


I honestly wouldn't say that typescript is a language, but rather a "mask", you write in typescript and then it is transpiled to the language in which it was implemented, in lua 5.2 it seems that 'continue' was implemented, lower versions, it should be some technique, like 'goto' for example.

In short, even php can support typescript, the question is, for what, since php supports typing/Classes.

In this case Typescript is indeed just a mask, but a well done mask. I can use array methods like filter, map, etc. and the transpiler will automatically call lua helper functions. When writing I feel like there is no Lua underneath
 
Back
Top