• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

How to use Total Initialization

Level 22
Joined
May 16, 2012
Messages
652
Hey Hivers! I'm back to cooking some good stuff for W3 again and now im porting the new stuff that i created and the stuff that i changed to Lua and to do so without worrying about script positions inside the editor i decided to try to use @Bribe Total Initialization script. Problem is, its not working or im not setting it up correctly. Here is the test map end the code im working right now. Any help is appreciated.


Lua:
OnInit("Class", function()
    Class = {}

    local initializers = {}
    local old = InitBlizzard

    function InitBlizzard()
        old()

        if initializers then
            for i = 1, #initializers do
                local this = initializers[i]

                if this.onInit and type(this.onInit) == "function" then
                    local ok, exception = pcall(this.onInit)

                    if not ok then
                        print("onInit Error: " .. tostring(exception))
                    end
                end
            end

            initializers = nil
        end
    end

    function Class:property(name, options)
        self.__props[name] = {
            get = options and options.get or nil,
            set = options and options.set or nil,
        }

        return self
    end

    function Class:allocate(...)
        local this = {}
        local class = self

        setmetatable(this, {
            __index = function(instance, key)
                local value = rawget(instance, key)
                local props = class.__props and class.__props[key]

                if value ~= nil then
                    return value
                end

                if key == "destroy" then
                    return function(self)
                        if not self.__destroyed then
                            self.__destroyed = true

                            for i = #instance.__destructors, 1, -1 do
                                rawget(instance.__destructors[i], "destroy")(self)
                            end

                            instance.__destructors = nil
                            Class.deallocate(self)
                        end
                    end
                end

                if props and props.get then
                    return props.get(instance)
                end

                return class[key]
            end,
            __newindex = function(instance, key, value)
                local props = class.__props and class.__props[key]

                if props and props.set then
                    return props.set(instance, value)
                end

                rawset(instance, key, value)
            end

        })

        return this
    end

    function Class.deallocate(self)
        setmetatable(self, { __mode = "k" })
    end

    setmetatable(Class, {
        __index = function(self, key)
            local getter = rawget(self, "_getindex")

            if getter then
                return getter(self, key)
            end

            return rawget(Class, key)
        end,
        __newindex = function(self, key, value)
            local setter = rawget(self, "_setindex")

            if setter then
                setter(self, key, value)
            else
                rawset(self, key, value)
            end
        end,
        __call = function(self, parent)
            local this

            if parent then
                this = setmetatable({ super = parent, __props = {} }, { __index = parent })

                if parent.__props then
                    for key, value in pairs(parent.__props) do
                        this.__props[key] = value
                    end
                end

                this.allocate = function(class, ...)
                    local instance
                    local current = class
                    local destructors = {}
                    local constructor = nil

                    repeat
                        if rawget(current, "destroy") then
                            table.insert(destructors, 1, current)
                        end

                        if not constructor and current.super and rawget(current.super, "create") then
                            constructor = current.super
                        end

                        current = current.super
                    until not current

                    if constructor then
                        instance = constructor:create(...)
                    else
                        instance = Class.allocate(class, ...)
                    end

                    instance.__destroyed = false
                    instance.__destructors = destructors

                    return instance
                end
            else
                this = setmetatable({ __props = {} }, { __index = self })
                this.__destroyed = false
                this.__destructors = { this }
            end

            table.insert(initializers, this)

            return this
        end
    })

    Class.create = Class.allocate
    Class.destroy = Class.deallocate
end)

Lua:
OnInit("Unit", function(requires)
    requires "Class"

    Unit = Class()

    local location

    Unit:property("x", { get = function(self) return GetUnitX(self.unit) end })

    Unit:property("y", { get = function(self) return GetUnitY(self.unit) end })

    Unit:property("z", { get = function(self)
        MoveLocation(location, GetUnitX(self.unit), GetUnitY(self.unit))
        return GetUnitFlyHeight(self.unit) + GetLocationZ(location)
    end })

    Unit:property("id", { get = function(self) return GetUnitUserData(self.unit) end })

    Unit:property("type", { get = function(self) return GetUnitTypeId(self.unit) end })

    Unit:property("handle", { get = function(self) return GetHandleId(self.unit) end })

    Unit:property("player", { get = function(self) return GetOwningPlayer(self.unit) end })

    Unit:property("armor", { get = function(self) return BlzGetUnitArmor(self.unit) end })

    Unit:property("mana", { get = function(self) return GetUnitState(self.unit, UNIT_STATE_MANA) end })

    Unit:property("health", { get = function(self) return GetWidgetLife(self.unit) end })

    Unit:property("agility", { get = function(self) return GetHeroAgi(self.unit, true) end })

    Unit:property("strength", { get = function(self) return GetHeroStr(self.unit, true) end })

    Unit:property("intelligence", { get = function(self) return GetHeroInt(self.unit, true) end })

    Unit:property("armortype", { get = function(self) return ConvertArmorType(BlzGetUnitIntegerField(self.unit, UNIT_IF_ARMOR_TYPE)) end })

    Unit:property("defensetype", { get = function(self) return ConvertDefenseType(BlzGetUnitIntegerField(self.unit, UNIT_IF_DEFENSE_TYPE)) end })

    Unit:property("isHero", { get = function(self) return IsUnitType(self.unit, UNIT_TYPE_HERO) end })

    Unit:property("isMelee", { get = function(self) return IsUnitType(self.unit, UNIT_TYPE_MELEE_ATTACKER) end })

    Unit:property("isRanged", { get = function(self) return IsUnitType(self.unit, UNIT_TYPE_RANGED_ATTACKER) end })

    Unit:property("isSummoned", { get = function(self) return IsUnitType(self.unit, UNIT_TYPE_SUMMONED) end })

    Unit:property("isStructure", { get = function(self) return IsUnitType(self.unit, UNIT_TYPE_STRUCTURE) end })

    Unit:property("isMagicImmune", { get = function(self) return IsUnitType(self.unit, UNIT_TYPE_MAGIC_IMMUNE) end })

    function Unit:destroy()
        self.unit = nil
    end

    function Unit:create(unit)
        local this = self:allocate()

        this.unit = unit

        return this
    end

    function Unit:onInit()
        location = Location(0, 0)
    end
end)

Lua:
OnInit("Spell", function(requires)
    requires "Class"
    requires "Unit"

    Spell = Class()

    Spell.struct = {}
    Spell.learned = {}
    Spell.sources = Unit:create(nil)
    Spell.targets = Unit:create(nil)

    function Spell:onInit()
        print("Spell onInit:" .. " Sources: " .. tostring(Spell.sources) .. " Targets: " .. tostring(Spell.targets))
    end
end)

As you will be able to deduce, Spell requires Unit and Class, Unit requires Class. I added a print statement on Spell:onInit to check it but im not seeing it in game. I know for a fact that it works because when im not using TI and just place the scripts in the reight order i see what i need.
 

Attachments

welcome back!

I've mostly stuck with TotalInitialization's basic set-up, i.e.
Lua:
local function myFunc()
    print("myFunc Initialization!")
end

OnInit.trig(myFunc)

But in your case, I don't recall reading about a specific MyClass:onInit() syntax, so I'd recommend just calling your initializer at the end manually. It'll still respect the order you specified due to your requires set-up at the beginning of the initializer.

Lua:
    function Spell:onInit()
        print("Spell onInit:" .. " Sources: " .. tostring(Spell.sources) .. " Targets: " .. tostring(Spell.targets))
    end

    Spell.onInit()
 
welcome back!

I've mostly stuck with TotalInitialization's basic set-up, i.e.
Lua:
local function myFunc()
    print("myFunc Initialization!")
end

OnInit.trig(myFunc)

But in your case, I don't recall reading about a specific MyClass:onInit() syntax, so I'd recommend just calling your initializer at the end manually. It'll still respect the order you specified due to your requires set-up at the beginning of the initializer.

Lua:
    function Spell:onInit()
        print("Spell onInit:" .. " Sources: " .. tostring(Spell.sources) .. " Targets: " .. tostring(Spell.targets))
    end

    Spell.onInit()

The onInit method is part of the Class functionality. It is automatically register when instantiating a new class object and it should run when the InitBlizzard is called, so its not really a part of the TI framework.

Lua:
        __call = function(self, parent)
            local this

            ....

            table.insert(initializers, this)

            return this
        end

Lua:
    local old = InitBlizzard

    function InitBlizzard()
        old()

        if initializers then
            for i = 1, #initializers do
                local this = initializers[i]

                if this.onInit and type(this.onInit) == "function" then
                    local ok, exception = pcall(this.onInit)

                    if not ok then
                        print("onInit Error: " .. tostring(exception))
                    end
                end
            end

            initializers = nil
        end
    end
 
ahh got it, sorry hadn't read through your class script.

so you're essentially trying to add your own MyClass:onInit() syntax that should automatically be ran for all classes inheriting from "Class". In that case, since you're using TotalInitialization, I recommend just doing this:
Lua:
    local initializers = {}

    function RunClassInitializers()
        if initializers then
            for i = 1, #initializers do
                local this = initializers[i]

                if this.onInit and type(this.onInit) == "function" then
                    local ok, exception = pcall(this.onInit)

                    if not ok then
                        print("onInit Error: " .. tostring(exception))
                    end
                end
            end

            initializers = nil
        end
    end

    OnInit.trig(RunClassInitializers)

...instead of overriding InitBlizzard. It is technically possible to override InitBlizzard, but due to scoping (and execution order, i.e. when your OnInit runs relative to main()), it won't run if you declare it inside the OnInit("Class", function() ... end) scope. You can always try moving it out of that scope to test it out, but afaik that may mess with TotalInitialization. So I'd either roll your own solution, or just stick with TI (strongly recommended) and use the pattern above^.

I also recommend adding DebugUtils to your map, because coding in Lua is often a nightmare without it. :thumbs_up:
 
ahh got it, sorry hadn't read through your class script.

so you're essentially trying to add your own MyClass:onInit() syntax that should automatically be ran for all classes inheriting from "Class". In that case, since you're using TotalInitialization, I recommend just doing this:
Lua:
    local initializers = {}

    function RunClassInitializers()
        if initializers then
            for i = 1, #initializers do
                local this = initializers[i]

                if this.onInit and type(this.onInit) == "function" then
                    local ok, exception = pcall(this.onInit)

                    if not ok then
                        print("onInit Error: " .. tostring(exception))
                    end
                end
            end

            initializers = nil
        end
    end

    OnInit.trig(RunClassInitializers)

...instead of overriding InitBlizzard. It is technically possible to override InitBlizzard, but due to scoping (and execution order, i.e. when your OnInit runs relative to main()), it won't run if you declare it inside the OnInit("Class", function() ... end) scope. You can always try moving it out of that scope to test it out, but afaik that may mess with TotalInitialization. So I'd either roll your own solution, or just stick with TI (strongly recommended) and use the pattern above^.

I also recommend adding DebugUtils to your map, because coding in Lua is often a nightmare without it. :thumbs_up:
yep, that works, thanks! But i noticed that the OnInit script must be at the top of the tree for it to work, which is kinda odd given its purpose hehe
 
Back
Top