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

Creating global variables in Lua (and some other Lua questions)

Status
Not open for further replies.

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Edit: I figured out my problem and managed to get the global variables and multi-dimensional array to work.

However, this brings up a new question. Is there a way to set the default values of these Arrays to be 0 instead of having to manually set every single one to 0 like I do below? I don't really want to loop from 1 to 32,000.

Code:
function Variables()
    mt = {}
    for i=1,10 do
      mt[i] = {}
      for j=1,10 do
        mt[i][j] = 0
      end
    end
end

This doesn't work either. When my unit attacks I want to pick every unit with 300 range of the attacked unit and kill them:
Code:
function GroupKill()
    local u = GetAttacker()
    local p = GetUnitLoc(GetAttackedUnitBJ())
    local g = CreateGroup()
    GroupEnumUnitsInRangeOfLoc(g, p, 300, nil)
    local t = GetEnumUnit()
    KillUnit(t)
end
How do I properly loop through every unit?
 
Last edited:
However, this brings up a new question. Is there a way to set the default values of these Arrays to be 0 instead of having to manually set every single one to 0 like I do below? I don't really want to loop from 1 to 32,000.

Blizzard added a way to create a table with a specific default value. instead of "{}" you use "__jarray(default value)".
Lua:
Data = {}
function Test()
   local i = 10
   repeat
       Data[i] = __jarray(32) -- will have 32 when not written on
       i = i - 1
   until i == 0

   Data[2][101] = 11

   print(Data[4][3000])
   print(Data[2][100])
   print(Data[2][101])
   print(Data[1][90100])
end

How do I properly loop through every unit?
Any way you did in jass. But you can also define the enum functions inside the ForGroup/ GroupEnumUnits line. That allows using the locals of the current function.

Lua:
function TestGroup()
   local unitGroup = CreateGroup()
   local text = "TestGroup: "
   GroupEnumUnitsOfPlayer(unitGroup, Player(0), nil)
   ForGroup(unitGroup, function()
       print(text..GetUnitName(GetEnumUnit())) --Display the local text and the unit names.
   end)
   DestroyGroup(unitGroup)
end
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Thanks for the info!

I have some more questions if you don't mind.

So here's something that I was messing with but I couldn't get to work properly. I wanted to basically pick every unit in a group and apply a Function to each of them. The other Function applies a 5 second Damage over Time effect to the picked unit. I tried this below but when I run DealDamage() inside of Attack(), DealDamage() runs for 5 seconds before Attack() resumes again. This means that if I Enumerated through 5 units, it would pick 1 unit, apply the DoT effect for 5 seconds, and then pick the 2nd unit AFTER the 5 seconds has passed. Is there an easy way to sort of separate the two functions so that all 5 picked units would have the DealDamage() function applied to them all at once? Sorry if this is confusing, I have no idea how to word this... I'm sure there's a lot better ways of doing this. Also, I read that I should use Coroutines instead of PolledWait but I haven't figured out how to do so yet.
Code:
function Attack()
    local u = GetAttacker()
    local p = GetUnitLoc(GetAttackedUnitBJ())
    local g = CreateGroup()
    GroupEnumUnitsInRangeOfLoc(g, p, 400, nil)
    for i=1,CountUnitsInGroup(g) do
        source = u
        target = GetEnumUnit()
        DealDamage()
    end
end

Code:
function DealDamage()
    local u = source
    local t = target
    for i=1,5 do
        PolledWait(1.00)
        UnitDamageTargetBJ(u, t, 20, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL)
    end
end
Note: I realize the function Attack() code wouldn't even work but I know how to fix it. I just threw this example code together quickly to convey the concept.
 
[code=Lua] Lua highlighting [/code]
I have not used Coroutine myself. But the conecpt I showed with ForGroup can also be used in TimerStart. One starts a new timer which executes a function that does a ForGroup the ForGroup also has a inner function which deals the damage. After the ForGroup one counts down a counter when the counter reaches 0 destroy the timer and the group. The counter has to be set at the start for sure.


Lua:
function Dot()
   local source = GetTriggerUnit()
   local targets = CreateGroup()
   local counter = 5
   local myCondition = Condition(function() return IsUnitEnemy(GetFilterUnit(),GetTriggerPlayer()) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) end)

   GroupEnumUnitsInRange(targets, GetUnitX(source), GetUnitY(source), 320, myCondition)
   DestroyCondition(myCondition)

   TimerStart(CreateTimer(), 1.0, true, function()
       ForGroup(targets, function() UnitDamageTarget(source, GetEnumUnit(), 20, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, nil) end)   
       counter = counter - 1
       if counter == 0 then
           local t = GetExpiredTimer()
           PauseTimer(t)
           DestroyTimer(t)
           GroupClear(targets)
           DestroyGroup(targets)
       end
   end)
end
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
One can get a coroutine to yield and wait for a timer to expire. This allows one to keep code linear by basically implementing a proper wait action that is not natively available.

In theory this could be expanded to other sorts of delays such as even waiting for a unit to die. Possibly multiple such events could be waited on at the same time with the actual event being specified by a passed argument.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Thanks for the help, I'm on a roll now re-creating everything in my map with Lua. Feels good!

Anyway, here's a new one for you guys. A simple Knockback system. I copied this straight from some JASS systems I found on here and tried to make it work with no luck.
Lua:
function Knockback()
    local u = KB_Source
    local t = KB_Target
    local dura = KB_Duration
    local dist = KB_Distance
    local speed = KB_Distance / 25
    local p1 = GetUnitLoc(u)
    local p2 = GetUnitLoc(t)
    local angle = AngleBetweenPoints(p1, p2)
    TimerStart(CreateTimer(), 0.04, true, function()
        dura = dura - 0.04
        x = GetUnitX(t)
        y = GetUnitY(t)
        x1 = ProjectX(x, speed, angle)
        y1 = ProjectY(y, speed, angle)
        SetUnitX(t, x1)
        SetUnitY(t, y1)
        if dura <= 0 then
            local e = GetExpiredTimer()
            PauseTimer(e)
            DestroyTimer(e)
            u = nil
            t = nil
        end
    end)
end
This doesn't move the unit at all. I'm stumped on how I should go about doing so.
I know the problem has to do with "ProjectX/Y" i'm just not sure what I'm doing wrong. Any ideas?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
I know the problem has to do with "ProjectX/Y" i'm just not sure what I'm doing wrong. Any ideas?
You did not post the code for ProjectX/Y. Without the code we can only try to guess what is wrong.

There is no need to nil u and t. Lua is not JASS2 and so does not suffer from nonsense like the local declared local agent variable reference counter leak on return bug.

I also do not see where variables like KB_Duration are declared. Be aware that Lua will throw no compile time error if one tries to use a variable which does not exist. Instead the function will throw an exception when executed which will be silent unless caught and printed by executing the function with pcall and checking the returned values.

Neither x1 or y1 are declared as local variables.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Ah, I didn't know there was more to ProjectX/Y. I just copied it from another Knockback script.

Anyway, I actually just tested this new code out and it worked perfectly. Is there anything else you see that I could change here? What about the local Point variables? Do I need to remove them and does setting them to nil work or do I have to use RemoveLocation?

Lua:
function Knockback()
    local u = KB_Source
    local t = KB_Target
    local dura = KB_Duration
    local dist = KB_Distance
    local speed = KB_Distance / 25
    local p1 = GetUnitLoc(u)
    local p2 = GetUnitLoc(t)
    local angle = AngleBetweenPoints(p1, p2)
    p1 = nil
    p2 = nil
    TimerStart(CreateTimer(), 0.04, true, function()
        dura = dura - 0.04
        local x = GetUnitX(t) + speed * Cos(angle * bj_DEGTORAD)
        local y = GetUnitY(t) + speed * Sin(angle * bj_DEGTORAD)
        SetUnitPosition(t, x, y)
        if dura <= 0 then
            local e = GetExpiredTimer()
            PauseTimer(e)
            DestroyTimer(e)
        end
    end)
end
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
You can use coordinates instead of locations: GetUnitX/Y SetUnitX/Y instead of Get/SetUnitPosition. Atan2(deltaY, deltaX) will give you the angle between two locations where deltaY = y2-y1 and deltaX = x2-x1.
Would I have to change these two lines to make it work? Because when I test it it doesn't work properly. I'm Printing out different Angles but the unit only ever moves in one direction.
Lua:
local x = GetUnitX(t) + speed * Cos(angle * bj_DEGTORAD)
local y = GetUnitY(t) + speed * Sin(angle * bj_DEGTORAD)

All I did was replace "local angle = AngleBetweenPoints(p1, p2)" with this:
Lua:
local x1 = GetUnitX(u)
local y1 = GetUnitY(u)
local x2 = GetUnitX(t)
local y2 = GetUnitY(t)
local angle = Atan2(y2 - y1, x2 - x1)
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Now I'm having trouble creating an item.

I tried all sorts of different Item ID's, "bspd", bspd, I000, "I000", nothing seems to work. It shouldn't have anything to do with the coordinates as I can move units there without a problem.
Lua:
local ix = GetUnitX(t) + 20 * Cos(angle)
local iy = GetUnitY(t) + 20 * Sin(angle)
local item = CreateItem(I000, ix, iy)
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Unfortunately, that didn't work either.

The Print appears but none of these codes work. I tried all 3 of them separately.
Lua:
function KnockbackSetup()
    print("Setup")
    CreateItem('i000', 0, 0)
    CreateItem('bspd', 0, 0)
    CreateItem('I000', 0, 0)
end
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
I tried all sorts of different Item ID's, "bspd", bspd, I000, "I000", nothing seems to work. It shouldn't have anything to do with the coordinates as I can move units there without a problem.
You need to manually convert them. Lua does not have non-standard nonsense like 4 character type codes.
'I000'
with the apostrophes
Lua is not JASS2. It does not natively support 4 character type codes. One has to either convert a string or provide a number

You can either unpack the string as a single integer number with the appropriate encoding pattern, or use the FourCC function to do something similar and possibly more efficiently.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Alright, this one might be negligible but which would perform better?

Should I use this to get the distance between a unit and an item:
Lua:
local x1 = GetUnitX(t)
local y1 = GetUnitY(t)
local x2 = GetItemX(KB_Item)
local y2 = GetItemY(KB_Item)
local distance = SquareRoot(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))

OR stick with points:
Lua:
p1 = GetUnitLoc(t)
p2 = GetItemLoc(KB_Item)
local distance = DistanceBetweenPoints(p1, p2)

I'd be running this code every 0.02 seconds.
 
Level 39
Joined
Feb 27, 2007
Messages
5,011
If you had bothered to look up DistanceBetweenPoints (a function in Blizzard.j, so not a native) you would see it does this:
JASS:
function DistanceBetweenPoints takes location locA, location locB returns real
    local real dx = GetLocationX(locB) - GetLocationX(locA)
    local real dy = GetLocationY(locB) - GetLocationY(locA)
    return SquareRoot(dx * dx + dy * dy)
endfunction
Using a location will never be more efficient than the coordinates directly.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
If you are checking the distance between a unit and a lot of items every 0.02 seconds then you will likely run into scaling issues due to the O(n) complexity of such tests. In such a case one should either enumerate items in a reasonably sized rect around the unit or write a custom positional data structure to make searching for items by distance more efficient.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
I'm only using one item to detect the pathing for my knocked-back units, but I'll look into what you said if it's heavy on the performance.

Edit: Deleted most of my original post after I figured out the problem to my question. Anyway, I was trying to get a coroutine to work and here is the solution!:
Lua:
function SunfireBlessing()
    local u = GetTriggerUnit()
    local cv = GetUnitUserData(u)
    local id = BlzGetAbilityIntegerField(BlzGetUnitAbility(u, GetSpellAbilityId()), ABILITY_IF_PRIORITY)
    local sfx = AddSpecialEffectTargetUnitBJ("weapon", u, "Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnMissile.mdl")
    local duration = 5.00

    if Spell[cv][id] ~= nil then
        print("End Previous Cast")
        coroutine.resume(Spell[cv][id])
    end

    Spell[cv][id] = coroutine.create( function ()
        print("Run Coroutine")
        PauseTimer(Timer[cv][id])
        DestroyTimer(Timer[cv][id])
        DestroyEffect(sfx)
        Spell[cv][id] = nil
    end)

    Timer[cv][id] = CreateTimer()
    TimerStart(Timer[cv][id], 0.10, true, function()
        duration = duration - 0.10
        print(duration)
        if duration <= 0 then
            coroutine.resume(Spell[cv][id])
        end
    end)
end
Using this setup I can now resume the coroutine "Spell[cv][id]" to either destroy the previous cast of my ability (giving it a replacing effect) or to destroy the current cast of my ability (when the timer expires). And I can do a hell of a lot more with it than just that. For example, if my caster were to die then I can immediately resume the coroutine and destroy the effects of this ability. This will make everything so much easier.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
I'm having trouble creating a 3 dimensional Array/Table. I found this code online but isn't there some way for me to get around having to loop from 1 to some large number?
Lua:
local x = {}
for i=1,m do
   x[i] = {}
   for j=1,n do
     x[i][j] = {}
     for k=1,p do
        x[i][j][k] = 0
     end
   end
end
I just want to initialize a 3 dimensional Array so that I can add values to it later on like this:

Initialize the Array
myArray[ , , ] = {}

Then change the values in another function:
Lua:
function TestAbility()
    local u = GetTriggerUnit()
    local cv = GetUnitUserData(u)
    local id = 3
    local count = 6
    myArray[cv][id][count] = A timer, unit, special effect, etc...
end
Is this possible?
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
I'm having trouble creating a 3 dimensional Array/Table. I found this code online but isn't there some way for me to get around having to loop from 1 to some large number?
The issue is that in Lua multidimensional arrays are implemented as nested tables. All these tables have to be initialized so that they can be indexed and a value returned.

I think the problem you are having is that you are trying to use Lua to perform tasks in a JASS like way. For example you are using unit user data to map data to a unit instead of mapping the data to the unit directly with a table set to have weak keys. This alone removes an entire tier of nesting reducing the 3D array to a 2D array which can be created when data is first mapped to a unit. If this 2D array has an index operating as a list of tables then one can further simplify it as such and create the final tables and append them to the list as required.

Unlike in JASS where one needed multiple arrays to each hold a separate data member of a complex type, in Lua a single table can be used mapping data member name to values. This allows a single table to represent a complete object, with references to the table acting as references to the object. A cast of an ability might translate into a single such table, if a single coroutine function execution is not sufficient to hold the data using local variables.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
I think I understand what you mean. So instead of using a Unit Indexer I could do something like this whenever I want to "Index" a new a unit:
Lua:
--Run this function when a unit enters the map
function SetUnitInfo()
    local info = {}
    info[1] = GetTriggerUnit()
    info[2] = 123
    info[3] = 456
    return print(info[1], info[2], info[3])
end
Now how would I go about referencing this information since it's in a local table?
For example, when I cast an ability and run a function like this:
Lua:
function TestAbility()
    local a = info[1]
    local b = info[2]
    local c = info[3]
    myArray[a][b][c] = A timer, unit, special effect, etc...
end
Or am I just not getting it... I was reading about metatables and keys but I'm not quite sure I understand how to take advantage of them. I'll keep messing with it, i'm sure it'll all make sense eventually.
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
So instead of using a Unit Indexer I could do something like this whenever I want to "Index" a new a unit:
You do something like this...

Code:
AbilityTable = {}
setmetatable(AbilityTable, {__mode = "k"}) --This instructs AbilityTable to use weak keys so as to not prevent key values from being collected by the garbage collector.

--Run this function when a unit enters the map
function SetUnitInfo()
    local info = {}
    local triggerunit = GetTriggerUnit()
    info[1] = 123
    info[2] = 456
    AbilityTable[triggerunit] = info
    return print(AbilityTable[triggerunit][1], AbilityTable[triggerunit][2])
end

Lua tables accept as good as every value as an index. Hence one can use a unit as an index in a table to look up data from. This is why no unit indexer is required when using Lua. It is similar to using a hashtable with a handle ID of a unit as a key, except a lot more convenient and less leak prone.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
@slash
I'll check it out, thanks. Also, would you happen to have an example code that you could share? Maybe an ability that you created using this stuff just so that I can get a better grasp of how to do it myself.

In the meantime here's what I have so far.
A simple ability that creates a special effect that lasts 5 for seconds. Here's the setup:
Lua:
--These are created at map initialization
AbilityTable = {}
setmetatable(AbilityTable, {__mode = "k"}) --This instructs AbilityTable to use weak keys
Lua:
--Run this function when a unit enters the map
function SetUnitInfo()
    local info = {}
    local u = GetTriggerUnit()
    AbilityTable[u] = info
end
And here's the ability function:
Lua:
function EnchantWeapon()
    local u = GetTriggerUnit()
    local id = GetSpellAbilityId()
    local sfx = AddSpecialEffectTargetUnitBJ("weapon", u, "Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnMissile.mdl")
    local duration = 5.00
    local co = 1
    local timer = 2

    --If the previous cast is still active (true) then destroy it
    if AbilityTable[u][id] == true then
        print("Destroy the Previous Cast")
        coroutine.resume(AbilityTable[u][co])
    end

    --Set the current cast to active (true)
    AbilityTable[u][id] = true

    --Create the coroutine that cleans up the ability
    AbilityTable[u][co] = coroutine.create( function ()
        print("Run Coroutine")
        PauseTimer(AbilityTable[u][timer])
        DestroyTimer(AbilityTable[u][timer])
        DestroyEffect(sfx)
        AbilityTable[u][id] = false
    end)

    --Create the timer that will run the coroutine when finished
    AbilityTable[u][timer] = CreateTimer()
    TimerStart(AbilityTable[u][timer], duration, false, function()
        coroutine.resume(AbilityTable[u][co])
    end)
end
The reason it's set up like this is so that you can only ever have 1 instance of the ability active at a time. So when you cast the ability again while it's previous cast is still running (timer is still on), it destroys the previous cast.

The ability does indeed work but i'm having trouble trying to store the coroutine and timer to my Unit. At the moment I set co/timer to integers instead of storing the information directly to the unit like I do with the Ability ID. Maybe something like this:
Lua:
AbilityTable[u][id][co] = create.coroutine...
--and
AbilityTable[u][id][timer] = CreateTimer()...
Unless of course there's an entirely better way of doing all of this.
 
Last edited:
Level 12
Joined
Oct 10, 2009
Messages
438
Sure thing, here's a typedef for Ship, it's also using a Vector2 type that I've made, but that's basically just a {x, y} object with some functions.

Lua:
Ship = {
    position = Vector:new(),
    -- Thrust towards a direction
    thrust = Vector:new(),
    -- Direction the ship is moving
    momentum = Vector:new(),

    mass = 1.0,
    acceleration = 50.0,

    velocity = 0.0,
    velocity_max = 500.0,
    velocity_min = -100.0,

    drag = 5,

    unit = nil,
}

function Ship:new (o)
    o = o or {}   -- create object if user does not provide one
    setmetatable(o, self)
    self.__index = self
    return o
end

function Ship:updateThrust(FPS)
    local shipFacing = GetUnitFacing(self.unit)
    local thrust = Vector:new()
    thrust.x = Cos(shipFacing * bj_DEGTORAD)
    thrust.y = Sin(shipFacing * bj_DEGTORAD)
    self.thrust = thrust:multiplyBy(self.velocity * self.mass)
end

function Ship:applyThrust(FPS)
    self.momentum = self.momentum:add(self.thrust):multiplyBy(0.9):max(self.velocity_max)
end

function Ship:updatePosition(FPS)
    -- Apply momentum and velocity
    self.position = self.position:add(self.momentum:multiplyBy(FPS))

    -- If we have a unit, update that unit's X and Y position
    SetUnitX(self.unit, self.position.x)
    SetUnitY(self.unit, self.position.y)
end

function Ship:alterVelocity(mod)
    self.velocity = self.velocity + self.acceleration * mod
    if self.velocity > self.velocity_max then
        self.velocity = self.velocity_max
        DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "ERR: Max velocity hit")
    elseif self.velocity < self.velocity_min then
        self.velocity = self.velocity_min
        DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "ERR: Min velocity hit")
    end
end


And here's the larger function block that creates and uses the ship object and functions.
If you haven't learnt much about Object Oriented Programming it can be a bit hard to grasp, but it basically allows you to let a Ship handle itself.
Object Oriented is the bomb, if you don't know much about it I highly suggest you read up on it, as it makes programming a bunch cleaner and saves a lot of code repetition.

Lua:
do
    -- How often does this trigger loop?
    local TRIGGER_FPS = 0.03
    local TRIGGER_TIMER = CreateTimer()

    -- Ability IDs for movement control
    local ABILITY_ID_DECREASE_SPEED = 1093677104
    local ABILITY_ID_INCREASE_SPEED = 1093677105

    -- Array holding all the ships
    local all_ships = {}
    local num_ships = 0

 
    function processShips()
        if num_ships == 0 then
            return
        end

        for i = 0, num_ships - 1 do
            local ship = all_ships
            ship:updateThrust(TRIGGER_FPS)
            ship:applyThrust(TRIGGER_FPS)
            ship:updatePosition(TRIGGER_FPS)
        end
    end

    function makeShipForUnit(unit)
        local result = Ship:new()
        result.unit = unit

        all_ships[num_ships] = result
        num_ships = num_ships + 1

        return result
    end

    function getShipForUnit(unit)
        if num_ships > 0 then
            for i = 0, num_ships do
                local ship = all_ships
                if ship.unit == unit then
                    return ship
                end
            end
        end
        -- If no ship is found we return a new one for it
        return makeShipForUnit(unit)
    end

    function accelerateShip(mod)
        local unit = GetTriggerUnit()
        local ship = getShipForUnit(unit)
  
        -- Apply thrust to direction ship is facing
        if ship == nil then
            DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "ERR: Ship is nill")
        else
            ship:alterVelocity(mod)
        end
    end
 
    --[[
        Hook into warcraft events
        MOVEMENT UPDATING
    --]]
    local ship_movement_trigger = CreateTrigger()
    TriggerRegisterTimerEventSingle(ship_movement_trigger, 1.0)
    TriggerAddAction(
        ship_movement_trigger,
        function() TimerStart(TRIGGER_TIMER, TRIGGER_FPS, true, processShips) end
    )
 
    --[[
        Hook into warcraft events
        INCREASE / DECREASE SPEED
    --]]
    local ship_accelerate_trigger = CreateTrigger()
    TriggerRegisterAnyUnitEventBJ(ship_accelerate_trigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    TriggerAddCondition(ship_accelerate_trigger,
        Condition(function()
            if GetSpellAbilityId() == ABILITY_ID_INCREASE_SPEED then
                accelerateShip(1)
            elseif GetSpellAbilityId() == ABILITY_ID_DECREASE_SPEED then
                accelerateShip(-1)
            end
            return false
        end)
    )
end


The script above essentially updates the ship's location every 0.03 seconds, and hooks into ability use events to change and alter each Ship's momentum.
 
Last edited:
Status
Not open for further replies.
Top