lolleko / gmod-typescript

Tool to generate typescript definitions for garrysmod.
MIT License
21 stars 8 forks source link

Missing Globals, Realms / States #8

Open TIMONz1535 opened 2 years ago

TIMONz1535 commented 2 years ago

There is no way to control script workflow with a SERVER/CLIENT realms. Its not exists in TypeScript.

https://wiki.facepunch.com/gmod/States

There are also some global variables, such as ENT, SWEP, GM. However, I have no idea how to implement them correctly, because they will be visible in the entire TypeScript code. TypeScriptToLua uses globalThis for _G alias. globalThis implemented by TypeScript itself.

https://wiki.facepunch.com/gmod/Global_Variables

Minor. Garry's mod also have some constants (vector_origin, vector_up) at the bottom of the page. I don't like their names, I'd rather make my own like ZeroVector, ForwardVector and other. But perhaps it can be useful to someone. I think you will decide for yourself whether you need to spend time on this.

lolleko commented 2 years ago

Adding server/client bools should is easy. They can just be added to https://github.com/lolleko/gmod-typescript/blob/master/types/extras.d.ts

ENT, SWEP, GM are a different story, however. These have some weird file based scoping. So declaring them globally can cause issues. Additionally, even if they would exist, it is impossible to access them properly in vanilla TS, in the way Gmod requires (function ENT:Spawn() end .....) again because of the odd file based scoping. We had some discussions about this on discord: https://discord.com/channels/515854149821267971/515854150257344524/571792067324280832 https://discord.com/channels/515854149821267971/515854150257344524/719667301086920717

I think we will need a custom decorator, similar to this one for DotA abilities https://github.com/ModDota/TypeScriptAddonTemplate/blob/master/src/vscripts/lib/dota_ts_adapter.ts#L34 https://github.com/ModDota/TypeScriptAddonTemplate/blob/master/src/vscripts/abilities/heroes/meepo/earthbind_ts_example.ts to attach functions from entity files to the ENT/SWEP table. Maybe even a custom TSTL plugin.

TIMONz1535 commented 2 years ago

I found this solution https://github.com/glua-addon-types/glua-types

https://github.com/SupinePandora43/Trailers-Reborn - there are no entities here... https://github.com/rwilliaise/gmodtf2sweps - it's almost empty here, but it gave me an idea how to implement a weapon class (see below)

declare const SERVER: boolean
declare const CLIENT: boolean
declare const MENU: boolean
declare const GAMEMODE: Gamemode
declare const GM: Gamemode

This means that they are available everywhere, and you must understand where you write your code. In general, it was the same on Lua...

I had to modify the declarations a bit

declare const SWEP: Weapon
declare const ENT: Entity
type Entity = IEntity & EntityFuncs & EntityHooks
type Weapon = IWeapon & WeaponFuncs & WeaponHooks

this allowed me to write like this

AddCSLuaFile()

SWEP.ViewModel = Model("models/weapons/c_arms_animations.mdl")
SWEP.WorldModel = Model("models/MaxOfS2D/camera.mdl")

SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = -1
SWEP.Primary.Automatic = false
SWEP.Primary.Ammo = "none"

and this

ENT.Spawnable = true
ENT.AdminOnly = true
ENT.PrintName = "Fog Editor"
ENT.Category = "Editors"

ENT.Initialize = function () {
    this.SetMaterial("gmod/edit_fog")

    if (CLIENT) {
    }

    this.UpdateTransmitState = function (this: Entity): TRANSMIT {
        return TRANSMIT.TRANSMIT_ALWAYS
    }
}

ENT.UpdateTransmitState = function () {
    return TRANSMIT.TRANSMIT_ALWAYS
}

ENT.SetMaterial = function (): string {
    return "wtf"
}

but all the functions that I write completely ignore the original declarations (except when some value is returned, like UpdateTransmitState), an example I can write anything in SetMaterial

I can write something more interesting, like this. In theory, it should automatically redirect everything to SWEP. I didn't test, just watched the Lua code. The constructor is needed only if I don't declare all values as static. The main thing is not to forget that this is a fake, not a real class.

declare const set: LuaTableSet<AnyNotNil, AnyNotNil, any>
interface ScriptedWeapon extends Entity { }
class ScriptedWeapon {
    // redirect for class methods from prototype and constructor
    __newindex(k: any, v: any) {
        if (k != "____constructor")
            set(SWEP, k, v)
    }
    // redirect for static variables from class
    static __newindex(k: any, v: any) {
        set(SWEP, k, v)
    }
}

class Pistol extends ScriptedWeapon {
    constructor(name: string) {
        super()

        this.Initialize = function () {
            this.SetMaterial("gmod/edit_fog")
        }
        this.PrintName = name
        this.AdminOnly = true
        this.Spawnable = true
    }

    Initialize(this: Entity): void {
    }

    UpdateTransmitState(this: Entity): TRANSMIT {
        return TRANSMIT.TRANSMIT_ALWAYS
    }

    SetupDataTables(this: Entity): void {
    }

    // you can easily make your own variables and methods
    // with SWEP/ENT you will have to cast it to `any` or `unknown`
    MyOwnValue1?: string
    MyOwnValue11: string = "kek"

    AdminOnly: boolean;
    PrintName: string = "Kek"
    static Category: string = "Kek2"
}
new Pistol("kekos")

Lua provides an opportunity to mutate the ENT methods/hooks, but I would like to implement the Entity. In a TypeScript, the implements requires all methods/properties... I'm not good at this :(

Oh yes, I haven't come up with anything smarter for client/server separation.

if (CLIENT) {
    class Pistol2 extends ScriptedWeapon {
        static Category: string = "Kek2"
        Initialize(this: Entity): void { }
        DrawModel(this: Entity): void { }
    }
    new Pistol2()
}
else {
    class Pistol2 extends ScriptedWeapon {
        static Category: string = "Kek2"
        Initialize(this: Entity): void { }
        OnRemove(this: Entity): void { }
    }
    new Pistol2()
}
lolleko commented 2 years ago

I added

declare const SERVER: boolean;
declare const CLIENT: boolean;
declare const MENU: boolean;
declare const GAMEMODE: Gamemode;
declare const GM: Gamemode;

declare const SWEP: Weapon;
declare const ENT: Entity;

in v0.3.0 unitl we can come up with something better. Also keep in mind that declarations for hooks are not yet generated.