• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Solved] Interface method triggers on Event

Status
Not open for further replies.
Level 17
Joined
Mar 21, 2011
Messages
1,597
Hi guys,

i want to create structs that extend a certain type. It has predetermined functions, and i want them to trigger on certain events. However, it doesnt work this way. Any ideas how i could realize that?


JASS:
interface i
    method onCast takes nothing returns nothing
endinterface

struct s extends i
    method onCast takes nothing returns nothing
    endmethod
endstruct

function cast
    call s_instance.onCast
endmethod

private function Init
    call TriggerRegisterUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT)
endfunction


JASS:
struct test extends s
    method onCast takes nothing return nothing
        //... do stuff when casting an ability
    endmethod
endstruct
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Try something like this
JASS:
library SpellLibrary uses Table, RegisterPlayerUnitEvent

    private interface SpellInterface
        method onSpellCast      takes ... returns ... defaults ...
        method onSpellChannel   takes ... returns ... defaults ...
        ...
    endinterface

    struct SpellStruct extends SpellInterface
        private static key table

        private static method onSpellEvent takes nothing returns nothing
            local thistype node = Table(table)[GetSpellAbilityId()]
            local eventid e = GetTriggerEventId()

            if e == EVENT_PLAYER_UNIT_SPELL_CAST then
                if node.onSpellCast.exists then
                    call node.onSpellCast(...)
                endif

            elseif e == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
                if node.onSpellChannel.exists then
                    call node.onSpellChannel(...)
                endif

            elseif ... then
                ...
            endif
        endmethod

        static method create takes integer abilityId returns thistype
            local thistype node = allocate()
            set Table(table)[abilityId] = node
            ...
            return node
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function onSpellEvent)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function onSpellEvent)
            ...
        endmethod
    endstruct

endlibrary

// Test
struct MySpell extends SpellStruct
    static integer SPELL_ABILITY_ID = 'A000'

    readonly static thistype instance = 0

    method onSpellCast takes ... returns ...
        call BJDebugMsg("Casting Spell")
    endmethod

    private static method onInit takes nothing returns nothing
        call create(SPELL_ABILITY_ID)
    endmethod
endstruct
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
JASS:
library SpellLibrary uses Table, RegisterPlayerUnitEvent

    private interface SpellInterface
        method onSpellCast      takes nothing returns nothing defaults nothing
        method onSpellChannel   takes nothing returns nothing defaults nothing
    endinterface

    struct SpellStruct extends SpellInterface
        private static key table

        private static method onSpellEvent takes nothing returns nothing
            local SpellInterface node = Table(table)[GetSpellAbilityId()] // Maybe it didn't work earlier because I used thistype instead of SpellInterface
            local eventid e = GetTriggerEventId()

            if e == EVENT_PLAYER_UNIT_SPELL_CAST then
                if node.onSpellCast.exists then
                    call node.onSpellCast()
                endif

            elseif e == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
                if node.onSpellChannel.exists then
                    call node.onSpellChannel()
                endif
            endif
        endmethod

        static method create takes integer abilityId returns thistype
            local thistype node = allocate()
            set Table(table)[abilityId] = node
            return node
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.onSpellEvent)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onSpellEvent)
        endmethod
    endstruct

endlibrary

// Test
struct MySpell extends SpellStruct
    static integer SPELL_ABILITY_ID = 'Strm'

    method onSpellCast takes nothing returns nothing
 
        call BJDebugMsg("Casting Spell")
    endmethod

    static method create takes nothing returns thistype
        return allocate(SPELL_ABILITY_ID)
    endmethod

    private static method onInit takes nothing returns nothing
        call create()
    endmethod
endstruct
This new script works for me ^

But this wouldnt work with multiple instances per spell?
It works but you have to manually allocate a new instance every time a spell is cast like:
JASS:
        private static method onSpellEvent takes nothing returns nothing
            local SpellInterface node = Table(table)[GetSpellAbilityId()]
            local eventid e = GetTriggerEventId()
            local SpellInterface instance = allocate() // <--

            if e == EVENT_PLAYER_UNIT_SPELL_CAST then
                if node.onSpellCast.exists then
                    call node.onSpellCast(instance) // <--
                endif

            elseif e == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
                if node.onSpellChannel.exists then
                    call node.onSpellChannel(instance) // <--
                endif
            endif
        endmethod
...
struct MySpell extends SpellStruct
...
    method onSpellCast takes thistype instance returns nothing
    // Simply refer to <instance> for a unique instance of cast
        call BJDebugMsg("Casting Spell")
    endmethod
endstruct
Notice that you have to also edit the interface methods signatures to do this
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
I dont get the signatures to match for some reason.
The method arguments and return types should match, but you can't do it like this:
JASS:
    private interface SpellInterface
        method onSpellCast      takes thistype instance returns nothing defaults nothing
        method onSpellChannel   takes thistype instance returns nothing defaults nothing
    endinterface
...
struct MySpell extends SpellStruct
    static integer SPELL_ABILITY_ID = 'Strm'

    method onSpellCast takes thistype instance returns nothing

        call BJDebugMsg("Casting Spell")
    endmethod
because the 'thistype' in the interface declaration and in the MySpell translates to different types (translates to SpellInterface and MySpell, respectively).

Instead try:
JASS:
    private interface SpellInterface
        method onSpellCast      takes SpellStruct instance returns nothing defaults nothing
        method onSpellChannel   takes SpellStruct instance returns nothing defaults nothing
    endinterface
...
struct MySpell extends SpellStruct
    static integer SPELL_ABILITY_ID = 'Strm'

    method onSpellCast takes SpellStruct instance returns nothing

        call BJDebugMsg("Casting Spell")
    endmethod
or something similar

By the way, what does "defaults ..." do?
It allows you to make the interface methods declaration optional for the inheriting struct.
Example if you have no onSpellChannel declaration in the MySpell struct, and the signature of onSpellChannel is like this:
method onSpellChannel takes integer a returns boolean defaults true
then when SpellStruct calls onSpellChannel() for an instance of type 'MySpell', jasshelper will generate a dummy method for 'MySpell' that has the same signature and returns the specified default value (true).
 
What are stub methods?
Check out the jass helper manual: JassHelper 0.A.0.0
It has all features of vjass explained with code examples.

Basically, stub methods are placeholder methods you define in a parent struct that get replaced in a child struct via the same name.
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
Check out the jass helper manual: JassHelper 0.A.0.0
It has all features of vjass explained with code examples.

Basically, stub methods are placeholder methods you define in a parent struct that get replaced in a child struct via the same name.

It seems like you always call the parent method as long as you call the method inside the parent struct?
would i have to pass the instance to a function outside the struct and then call instance.onWhatever()?
 
No stub methods only get called if the instance is actually of parent type only or no child method of the same name is specified by that child. Otherwise only the replacing child method gets called.

So if for example the struct is a child type but gets called via parent.methodname, even then the child method will be called.
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
Here is an example
JASS:
struct parent

    stub method onCreate takes nothing returns nothing
        call BJDebugMsg("parent")
    endmethod

    static method create takes nothing returns thistype
        local thistype this = thistype.allocate()
        call this.onCreate()
        return this
    endmethod
 
endstruct

struct child extends parent

    method onCreate takes nothing returns nothing
        call BJDebugMsg("child")
    endmethod

endstruct

function Init takes nothing returns nothing
    local child c = child.create() // <-- "parent" will be displayed, but i want "child" to display
endfunction


Maybe this will fix it? I dont have an editor right now
JASS:
struct parent

    stub method onCreate takes nothing returns nothing
        call BJDebugMsg("parent")
    endmethod

    static method create takes nothing returns thistype
        local thistype this = thistype.allocate()
        call OnCreate(this)
        return this
    endmethod
  
endstruct

function OnCreate takes parent p returns nothing
    call p.onCreate()
endfunction

struct child extends parent

    method onCreate takes nothing returns nothing
        call BJDebugMsg("child")
    endmethod

endstruct

function Init takes nothing returns nothing
    local child c = child.create()
endfunction

EDIT: So.. the problem seems to be the call inside method create. It does not work for both interface and stub method.
 
Last edited:

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
JASS:
struct parent

    stub method onCreate takes nothing returns nothing
        call BJDebugMsg("parent")
    endmethod

    static method create takes nothing returns thistype
        local thistype this = thistype.allocate()
        call this.onCreate()
        return this
    endmethod
 
endstruct
I think the reason why this calls the parent method is because at that stage above, the 'type' of the allocated instance is still set to the type of the parent since parent allocator is called first. It will only later be set to the 'type' of the child struct once the create() of the parent returns and it goes to the body of the allocator of the child struct. Jasshelper actually tracks the 'type' of the instances in an array variable.
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
Hmm ok then. This might also be connected with this problem:


This does destroy "Something s"
JASS:
struct A

    Something s
 
    static method create takes nothing returns thistype
        local thistype this = thistype.allocate()
        set s = Something.create()    // <-- variable assigned on create
        return this
    endmethod
 
    method destroy takes nothing returns nothing
        call this.s.destroy()
        call this.deallocate()
    endmethod
 
endstruct


struct B extends A
endstruct


function foo takes nothing returns nothing
    local B b = B.create()
    call b.destroy()
endfunction


This does NOT destroy "Something s"
JASS:
struct A

    Something s
 
    method start takes nothing returns nothing
        set s = Something.create() // <-- variable assigned on some other method
    endmethod
 
    static method create takes nothing returns thistype
        local thistype this = thistype.allocate()
        call this.start()
        return this
    endmethod
 
    method destroy takes nothing returns nothing
        call this.s.destroy()
        call this.deallocate()
    endmethod
 
endstruct


struct B extends A
endstruct


function foo takes nothing returns nothing
    local B b = B.create()
    call b.destroy()
endfunction

I would like to have method start, is there a way to fix this?


Edit: It does work, i made a mistake
 
Last edited:
Level 20
Joined
Aug 13, 2013
Messages
1,696
Do you mean you want a start() constructor instead of create()?
You can just have multiple constructors as long they're static.

Sometimes, the next actions you're doing after instantiating a struct is not always instant.
So setting members or calling some methods after create() wouldn't run at the same time.
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
Do you mean you want a start() constructor instead of create()?
You can just have multiple constructors as long they're static.

Sometimes, the next actions you're doing after instantiating a struct is not always instant.
So setting members or calling some methods after create() wouldn't run at the same time.

No i want a method that runs after setup.

local A a = A.create()
set a.unit = GetTriggerUnit()
set a.x = 0
set a.y = 1
call a.start()

EDIT: nvm. It works just fine, i ran start() twice LOL
 
Status
Not open for further replies.
Top