• 🏆 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!

Of vJASS and Structs

Status
Not open for further replies.
Allow me to preface this by saying that I tried reading a tutorial on Structs and... well... "What?"

I'm not a JASSer, even less a vJASSer, but I have used some vJASS resources in the past with relative success. That being said, reading actual code feels like trying to read Mandarin half the time.

I need help understanding how to use this system. In essence, I want to make a unit fire a missile whenever it deals damage (I'm using Bribe's Damage Engine for that). I tried looking at the code on the demo map (LAB4 Target, more specifically) and I couldn't find any of the familiar GUI markers to help me locate myself through the trigger, which I'd say is the first habit I need to undo. Moreover, I have no idea what's supposed to be triggering the missile creation.

That being said, I did take a long hard look at the code and the very vague conclusions (that are probably wrong) I came to were: structs are like triggers within triggers, that can be 'connected' to an object. The Methods inside the Struct serve as events that occur only to the object the Struct is tied to. However, what I read on structs say it's like a custom type that you can create.

What really, really confuses me is that I have NO IDEA how anything connects to anything in the code to go from something like 'if unit is selected, create missile every X seconds'.

I guess what I'm asking is... how do I code this so a missile is created when a unit deals damage?
 
Nestharus is right, you got it all wrong. I'll try to explain as short as possible how they work:

Imagine that you want to make a missile system. You realize that each missile will need some variables, such as their velocity, target, and dummy unit. The best way to solve this is by using an array for each variable, and then give each missile a unique index to use for the array. Like this:

JASS:
    real array missile_vel_x
    real array missile_vel_y
    unit array missile_dummy
    unit array missile_target
    integer missile_count = 0 //Keep track on our number of missiles

Now, when you want a new missile, you can do roughly like this:

JASS:
function NewMissile takes player owner, real x, real y, real vel, unit target returns nothing
    local real dx = GetUnitX(target)-x
    local real dy = GetUnitY(target)-y
    local real dist = SquareRoot(dx*dx + dy*dy)

    set missile_target[missile_count] = target
    set missile_dummy[missile_count] = CreateUnit() //not gonna write all the parameters, but you get it
    set missile_vel_x[missile_count] = (dx/dist)*vel //Basic vector math
    set missile_vel_y[missile_count] = (dy/dist)*vel
    set missile_count = missile_count+1
endfunction

If you want to move them forward, you can now periodically loop through all missiles from index 0 to missile_count. There is one problem though - when a missile "dies", its index is unused, and missile_count will eventually reach the maximum allowed array index. There are some nifty logic tricks for solving this, but we don't need to bother with it, because we have structs!

This is how the code above would look using structs:

JASS:
struct Missile
    //These variables are called "members", and they will
    //be converted into global arrays.

    unit dummy
    unit target
    real vel_x
    real vel_y
    
    //We no longer need the "count" variable, since structs
    //have built-in functionality for this.

    static method create takes player owner, real x, real y, real vel, unit target returns Missile
        //This function creates a new "instance".
        //"static method" is just a fancy name for "function".
        //However, you call them in a different way: structname.methodname()
        //Non-static methods are called like this: instancename.methodname()

        local Missile this = Missile.allocate() //"Allocate" is a method which returns a new, free index. 
        //"this" is just an integer variable (but we call it a "Missile").

        local real dx = GetUnitX(target)-x //Nevermind this, it is just for getting the velocity vector.
        local real dy = GetUnitY(target)-y
        local real dist = SquareRoot(dx*dx + dy*dy)

        set this.dummy = CreateUnit()
        set this.target = target
        set this.velx = (dx/dist)*vel
        set this.vely = (dy/dist)*vel

        return this //Return the newly created missile index!
    endmethod

endstruct

You can use methods to manipulate your struct instances. For instance, you could do something like this:

JASS:
local Missile newmissile = Missile.create(Player(0), 0, 0, 0, null) //"create" is a static method. It is called upon the struct itself.
//Note that "newmissile" is now a variable of type "Missile" (which is actually just an integer). The value it holds is your array index, or "instance".
call newmissile.move() //"move" is not a static method. It is called on an instance.

There is much more to learn, but my advice is that you try to read the JassHelper manual.
 
Okay, so after watching a bunch of Nestharus's vJASS vids (dear lord you type fast) and copy/pasting code from BPower's Missile, I have the following WIP:

I took this trigger and converted to custom text to get the basic framework for the JASS below.

  • Missile Test BCKUP
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • DamageEventType Equal to 0
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of DamageEventSource) Equal to Rifleman
        • Then - Actions
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of DamageEventSource) Equal to Dragonhawk Rider
        • Then - Actions
        • Else - Actions
What I'm attempting to do here is make either a Rifleman or Dragonhawk launch a missile whenever they inflict damage. I'm using Bribe's Damage Engine v3.0.0.0 for that.

Now, it still doesn't work, but at least JassHelper isn't yelling, "ERRORS!" at me anymore.
JASS:
scope Test //BPower's Missile example had 'Initializer Init' here. What does that mean?

    globals
        private unit Target = udg_DamageEventTarget
        private unit Source = udg_DamageEventSource
        private real Damage = udg_DamageEventPrevAmt
    endglobals

   private struct MissileTry extends array
        
        private static method onFinish takes Missile this returns boolean
            call UnitDamageTarget( Source, Target, Damage, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null )
            //Natives with BJ are apparently the devil's work, so I'm using this instead. Thing is, idk any weapon types, so I just wrote null instead
            return true
        endmethod
    
        implement MissileStruct //What is the purpose of this line?
        
    endstruct
    
    function fireMissile takes nothing returns nothing
        local Missile m 
        
        //call TriggerSleepAction(.5) <-- what is this for???
        set m           = Missile.create(140, -670, 50, 90*bj_DEGTORAD, 2000, 50)
        set m.speed     = 15.
        set m.model     = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
        set m.arc       = 0.15
        set m.target    = Target
        call MissileTry.launch(m)
    endfunction

    //Conditions?
    function Trig_Missile_Test_Conditions takes nothing returns boolean
        if ( not ( udg_DamageEventType == 0 ) ) then
            return false
        endif
        return true
    endfunction

    function Trig_Missile_Test_Func001C takes nothing returns boolean
        if ( not ( GetUnitTypeId(udg_DamageEventSource) == 'hrif' ) ) then

            call fireMissile() //I have no idea
        
            return false
        endif
        return true
    endfunction

    function Trig_Missile_Test_Func002C takes nothing returns boolean
        if ( not ( GetUnitTypeId(udg_DamageEventSource) == 'hdhw' ) ) then
            return false
        endif
        return true
    endfunction

    //Actions... maybe?
    function Trig_Missile_Test_Actions takes nothing returns nothing
        if ( Trig_Missile_Test_Func001C() ) then
        else
        endif
        if ( Trig_Missile_Test_Func002C() ) then
        else
        endif
    endfunction

    //Events... I think
    function InitTrig_Missile_Test takes nothing returns nothing
        set gg_trg_Missile_Test = CreateTrigger(  )
        call TriggerRegisterVariableEvent( gg_trg_Missile_Test, "udg_DamageEvent", EQUAL, 1.00 )
        call TriggerAddCondition( gg_trg_Missile_Test, Condition( function Trig_Missile_Test_Conditions ) )
        call TriggerAddAction( gg_trg_Missile_Test, function Trig_Missile_Test_Actions )
    endfunction

endscope

I wrote a bunch of comments in the code for things I'm still very much confused by. Anyone care to enlighten me?

PS: should I be using the trigger setup with the bunch of //! textmacrowhatever things like in Nestharus's vids?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
//BPower's Missile example had 'Initializer Init' here. What does that mean?
An initializer executes a function while the map is in loading screen.
It does add call ExecuteFunc("name") to you map script.
The functionality closest to an initializer in GUI is the OnMapInitialization event.
( The GUI OnMapInit event however doesn't use the ExecuteFunc native )

implement MissileStruct //What is the purpose of this line?
Basically it does copy n' paste some code which I wrote to make Missile working.
It adds 2 things to your struct:
1. Basics methods required to launch a Missile.
2. Checks what you wrote into the struct and add required code, so everything is working.

//call TriggerSleepAction(.5) <-- what is this for???
Just for the demo, don!t bother this at all.

I improve the manual of Missile soon. It is part of the Missile thread. ( you saw it? )
 
I did read the Missile thread, but considering I know close to nothing about Structs (or JASS for that matter), I was still confused :p. It's only after watching a few tutorial videos on vJASS that I kinda have an inkling of what to do... which in retrospect still isn't much :D

But, baby steps. Now to figure out how to make the missile shoot.
 
The "implement" part is related to modules. It is exactly as BPower says - it's copy 'n paste. Usually, you create modules for parts of the struct that you want to be optional, "extra features", or for things that would appear in multiple different structs. For instance, you might want to organize your structs using a linked list structure - then, you can just implement a linked list module to it. Modules really have no benefits other than keeping your code clean and organized. Same goes for textmacros (which i generally dislike using due to their ugliness).
 
Status
Not open for further replies.
Top