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

Jass Unit Scale

Status
Not open for further replies.
I'm having the issue with this script that returns units it moves to the default 1.0 scale instead of the scale listed in the object editor.

In the test map you see that footmen are set to 1.5 and after being moved they will be the default size that you see in vanilla WC3.

JASS:
scope Waygate initializer Init
//made by MaskedPoptart
// requires Table, TimerUtils
private keyword Waygate

globals
    private constant integer UNSELECTABLE_ABIL_ID = 'Aloc'
    private constant integer INVULNERABLE_ABIL_ID = 'Avul'
    
    private constant integer WAYGATE_UNIT_ID = 'u000'
    private constant integer REQUIREMENT_DUMMY_ID = 'e000'
    private constant integer BUILD_WAYGATE_ABIL_ID = 'A000'
    private constant real ACTIVATION_DELAY = .5 //after a waygate finishes construction, it
    //will ignore all units that enter it for this amount of time (seconds).
    private constant real ENTER_RANGE = 75.0    //units have to come this close to be sent through the waygate
    private constant real TIMER_INTERVAL = .03  //units change scale every TIMER_INTERVAL seconds
    private constant real SCALE_PER_SECOND = .7 //this is how much scale units change every second (not affected by TIMER_INTERVAL)
    private constant real DISTANCE_PER_SECOND = 1000.0  //this is how fast units are transported between portals
    
    //--------------------------------------------
    //don't touch anything from this line on
    private constant integer IGNORE = 1
    private constant integer DONT_IGNORE = 0
    
    private unit array playerWaygateRequirement
    private Waygate array playerWaygates
    private boolexpr isValidEnteringUnitExpr
endglobals

//----------------------Helper Functions-----------------------
private function CreateRequirementForPlayer takes player p returns nothing
    local unit u = CreateUnit(p, REQUIREMENT_DUMMY_ID,/*
    */ GetPlayerStartLocationX(p), GetPlayerStartLocationY(p), bj_UNIT_FACING)
    call UnitAddAbility(u, INVULNERABLE_ABIL_ID)
    call UnitAddAbility(u, UNSELECTABLE_ABIL_ID)
    set playerWaygateRequirement[GetPlayerId(p)] = u
    set u = null
endfunction

private function CreateRequirements takes nothing returns nothing
    local integer i = 0
    loop
        call CreateRequirementForPlayer(Player(i))
        set i = i + 1
        exitwhen i == bj_MAX_PLAYER_SLOTS
    endloop
endfunction

private function RemoveRequirementForPlayer takes player p returns nothing
    local integer pId = GetPlayerId(p)
    call RemoveUnit(playerWaygateRequirement[pId])
    set playerWaygateRequirement[pId] = null
endfunction

//----------------------Main Section------------------------
private struct TransportationData
    private unit unitToTransport
    private real scale
    private Waygate startWaygate
    private Waygate endWaygate
    private timer t
    
    private static method enlargeUnitLoop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype this = thistype(GetTimerData(t))
        local unit u = this.unitToTransport
        local real scale = this.scale
        if(scale <= 1.0)then
            call SetUnitScale(u, scale, scale, scale)
            set this.scale = scale + SCALE_PER_SECOND*TIMER_INTERVAL
        else
            call SetUnitScale(u, 1.0, 1.0, 1.0)
            set this.scale = 1.0
            call ReleaseTimer(t)
            call UnitRemoveAbility(u, INVULNERABLE_ABIL_ID)
            call PauseUnit(u, false)
            call this.destroy()
        endif
    endmethod
    
    private static method transport takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype this = thistype(GetTimerData(t))
        local unit unitToTransport = this.unitToTransport
        local unit source = this.startWaygate.waygateUnit
        local Waygate destinationWaygate = this.endWaygate
        local unit destination = endWaygate.waygateUnit
        if(destination != null and destinationWaygate.isActivated)then
            set destinationWaygate.unitsToIgnore[unitToTransport] = IGNORE //makes the other waygate ignore the fact that this
                //unit is entering its range.
            call SetUnitPosition(unitToTransport, GetUnitX(destination), GetUnitY(destination))
            call TimerStart(t, TIMER_INTERVAL, true, function thistype.enlargeUnitLoop)
        elseif(source != null)then
            call TimerStart(t, TIMER_INTERVAL, true, function thistype.enlargeUnitLoop)
        else
            call SetUnitExploded(unitToTransport, true)
            call KillUnit(unitToTransport)
        endif
    endmethod
    
    private static method shrinkUnitLoop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype this = thistype(GetTimerData(t))
        local unit unitToTransport = this.unitToTransport
        local Waygate sourceWaygate
        local unit source
        local Waygate destinationWaygate
        local unit destination
        local real scale = this.scale
        local real dx
        local real dy
        local real transportTime
        if(scale >= 0)then
            call SetUnitScale(unitToTransport, scale, scale, scale)
            set this.scale = scale - SCALE_PER_SECOND*TIMER_INTERVAL
        else
            call SetUnitScale(unitToTransport, 0.0, 0.0, 0.0)
            set this.scale = 0.0
            call PauseTimer(t)
            set sourceWaygate = this.startWaygate
            set source = sourceWaygate.waygateUnit
            set destinationWaygate = this.endWaygate
            set destination = destinationWaygate.waygateUnit
            if(destination != null and destinationWaygate.isActivated)then
                set dx = GetUnitX(destination) - GetUnitX(source)
                set dy = GetUnitY(destination) - GetUnitY(source)
                set transportTime = (dx*dx + dy*dy) / (DISTANCE_PER_SECOND*DISTANCE_PER_SECOND)
                call TimerStart(t, transportTime, false, function thistype.transport)
            elseif(source != null)then
                call TimerStart(t, TIMER_INTERVAL, true, function thistype.enlargeUnitLoop)
            else
                call SetUnitExploded(unitToTransport, true)
                call KillUnit(unitToTransport)
            endif
        endif
    endmethod
    
    static method create takes unit unitToTransport, Waygate startWaygate, Waygate endWaygate returns thistype
        local thistype this = thistype.allocate()
        local timer t = NewTimer()
        set this.unitToTransport = unitToTransport
        set this.scale = 1.0
        set this.startWaygate = startWaygate
        set this.endWaygate = endWaygate
        call UnitAddAbility(unitToTransport, INVULNERABLE_ABIL_ID)
        call PauseUnit(unitToTransport, true)
        call SetTimerData(t, this)
        call TimerStart(t, TIMER_INTERVAL, true, function thistype.shrinkUnitLoop)
        set this.t = t
        return this
    endmethod
endstruct

private struct Waygate
    readonly unit waygateUnit
    private Waygate linkedWaygate
    
    readonly boolean isActivated
    private trigger entranceTrigger
    readonly HandleTable unitsToIgnore
    
    private static HandleTable instances
    
    private method onDestroy takes nothing returns nothing
        local unit toRemove = this.waygateUnit
        local player owner = GetOwningPlayer(toRemove)
        local integer ownerId = GetPlayerId(owner)
        local thistype linkedWaygate = this.linkedWaygate
        if(linkedWaygate != 0)then //if the player has two waygates
            call CreateRequirementForPlayer(owner)
            set linkedWaygate.linkedWaygate = 0 
            set playerWaygates[ownerId] = linkedWaygate
        endif
        if(playerWaygates[ownerId] == this)then //if the stored waygate is this
            set playerWaygates[ownerId] = this.linkedWaygate //set stored waygate to other waygate
        endif
        if(this.entranceTrigger != null)then
            call DestroyTrigger(this.entranceTrigger)
            set this.entranceTrigger = null
        endif
        set this.linkedWaygate = 0
        set this.waygateUnit = null
    endmethod
    
    private static method startTransportToLinkedWaygate takes nothing returns nothing
        local unit unitToTransport = GetTriggerUnit()
        local thistype this = thistype( thistype.instances[GetTriggeringTrigger()] )
        if(this.isActivated)then
            if(this.unitsToIgnore[unitToTransport] == IGNORE)then
                set this.unitsToIgnore[unitToTransport] = DONT_IGNORE
            else
                call TransportationData.create(unitToTransport, this, this.linkedWaygate)
            endif
        endif
    endmethod
    
    private static method remove takes nothing returns nothing
        local trigger trig = GetTriggeringTrigger()
        local thistype this = thistype( thistype.instances[trig] )
        call DestroyTrigger(trig)
        call this.destroy()
    endmethod
    
    private static method activate takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype this = thistype( GetTimerData(t) )
        set this.isActivated = true
        call ReleaseTimer(t)
    endmethod
    
    static method finishConstruction takes unit builder, unit waygateUnit returns nothing
        local trigger trig
        local timer t
        local thistype this = thistype( thistype.instances[waygateUnit] )
        set t = NewTimer()
        call SetTimerData(t, this)
        call TimerStart(t, ACTIVATION_DELAY, false, function thistype.activate)
        
        set trig = CreateTrigger()
        call TriggerRegisterUnitInRange(trig, waygateUnit, ENTER_RANGE, isValidEnteringUnitExpr)
        call TriggerAddAction(trig, function thistype.startTransportToLinkedWaygate)
        set this.entranceTrigger = trig
        set thistype.instances[trig] = this
        
        set trig = CreateTrigger()
        call TriggerRegisterUnitEvent(trig, waygateUnit, EVENT_UNIT_DEATH)
        call TriggerAddAction(trig, function thistype.remove)
        set thistype.instances[trig] = this
    endmethod
    
    static method cancelConstruction takes unit waygateUnit returns nothing
        local thistype this = thistype( thistype.instances[waygateUnit] )
        call this.destroy()
    endmethod
    
    static method startConstruction takes unit waygateUnit returns nothing
        local thistype this = thistype.allocate()
        local player owner = GetOwningPlayer(waygateUnit)
        local integer ownerId = GetPlayerId(owner)
        local thistype otherWaygate = playerWaygates[ownerId]
        set this.waygateUnit = waygateUnit
        if(otherWaygate != 0)then //if there are now 2 waygates owned by the player
            set this.linkedWaygate = otherWaygate
            set otherWaygate.linkedWaygate = this
            call RemoveRequirementForPlayer(owner) //don't allow player to make any more waygates
        else
            set this.linkedWaygate = 0
            set playerWaygates[ownerId] = this
        endif
        set this.entranceTrigger = null
        set this.isActivated = false
        set this.unitsToIgnore = HandleTable.create()
        set thistype.instances[waygateUnit] = this
    endmethod
    
    private static method onInit takes nothing returns nothing
        set thistype.instances = HandleTable.create()
    endmethod
endstruct

private function StartWaygate takes nothing returns nothing
    call Waygate.startConstruction(GetConstructingStructure())
endfunction

private function CancelWaygate takes nothing returns nothing
    call Waygate.cancelConstruction(GetCancelledStructure())
endfunction

private function FinishWaygate takes nothing returns nothing
    call Waygate.finishConstruction(GetTriggerUnit(), GetConstructedStructure())
endfunction

//--------------------Conditions/Filters---------------------------

private function IsValidEnteringUnit takes nothing returns boolean
    return not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND)
endfunction

private function IsBuildingWaygate takes unit building returns boolean
    return GetUnitTypeId(building) == WAYGATE_UNIT_ID
endfunction

private function IsStartedBuildingWaygate takes nothing returns boolean
    return IsBuildingWaygate(GetConstructingStructure())
endfunction

private function IsCancelledBuildingWaygate takes nothing returns boolean
    return IsBuildingWaygate(GetCancelledStructure())
endfunction

private function IsFinishedBuildingWaygate takes nothing returns boolean
    return IsBuildingWaygate(GetConstructedStructure())
endfunction

//-------------------------Init-----------------------
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_CONSTRUCT_START)
    call TriggerAddCondition(trig, Condition(function IsStartedBuildingWaygate))
    call TriggerAddAction(trig, function StartWaygate)
    
    set trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL)
    call TriggerAddCondition(trig, Condition(function IsCancelledBuildingWaygate))
    call TriggerAddAction(trig, function CancelWaygate)
    
    set trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
    call TriggerAddCondition(trig, Condition(function IsFinishedBuildingWaygate))
    call TriggerAddAction(trig, function FinishWaygate)
    
    call CreateRequirements()
    
    set isValidEnteringUnitExpr = Filter(function IsValidEnteringUnit)
endfunction

endscope
 

Attachments

  • Tunnel.w3x
    122 KB · Views: 129
try changing this: local thistype this = thistype(GetTimerData(t) to this: local thistype this = GetTimerData(t)


JASS:
/*anyway, these lines makes the unit's scale to 1.0
  since they are ABSOLUTE VALUES and NOT RELATIVE to the
  default values in the editor
*/
call SetUnitScale(u, 1.0, 1.0, 1.0)
set this.scale = 1.0

and you leak some locals... ^_^
 
Level 8
Joined
Aug 4, 2006
Messages
357
This bug is impossible to fix without significant work-arounds, due to the fact that Blizzard included no GetUnitScale function (even though they included a SetUnitScale function... wth?!).

The significant work-around:
How it would affect the user:
Any map that used this library would have to make all placed units have scale 1.0, and change their scale to the desired amount through triggers, right after each unit is placed. This would be a MAJOR pain in the ass since even Blizzard-created units have scale != 1.

How Kam would have to change the library:
You would have to make a hook function for SetUnitScale (i.e. a function that get's called whenever SetUnitScale is called) that records each unit's new scale into a hashtable. Then you could create a GetUnitScale function which basically reads from the hashtable.
 
Status
Not open for further replies.
Top