• 🏆 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] Function Exits Early

Status
Not open for further replies.
Level 16
Joined
Mar 3, 2006
Messages
1,564
When I increase the size of the inventory or the number of players or both the code exits early, otherwise everything works fine.

JASS:
    globals

        string BIG_MSG // for testing only

        @integer PLAYER_INGAME_COUNT = 1@

        hashtable Data   = InitHashtable()

        string TRACK_MODEL        = "war3mapImported\\Track75.mdx"
        integer INV_ABILITY_ID    = 'I000'
        integer BASE_MODEL_ID     = 'B000'
        integer SELECTOR_MODEL_ID = 'B001'

        @integer MAX_ROW_COUNT = 25@
        @integer MAX_COL_COUNT = 25@
        integer MAX_SIZE      = MAX_ROW_COUNT * MAX_COL_COUNT
        integer MAX_ITEMS

        integer rowKey  = 1
        integer colKey  = 21
        integer trackKey = 41
        integer itemTableKey = -100

        real OFFSET_X = 83.75
        real OFFSET_Y = OFFSET_X//84.00
        real BORDER_SIZE = 100.00
        real ICON_WIDTH = 80.00
        real ICON_HEIGHT = 80.00
        real BASE_X =  - 2816
        real BASE_Y =    2816

        real H = 1.3 * RMaxBJ(( OFFSET_X * ( MAX_COL_COUNT - 1 ) )+ICON_WIDTH , ( OFFSET_Y * ( MAX_ROW_COUNT - 1 ) )+ICON_HEIGHT )

        trigger onClick  = CreateTrigger()
        trigger onTrack  = CreateTrigger()
        trigger onOpen   = CreateTrigger()
        trigger onAquire = CreateTrigger()

        timer cameraLockTimer = CreateTimer()

        integer array groundItemId
        integer array itemIcon
        integer array itemType
        ability array itemEffect
        string  array itemName
        string  array itemDescription

        boolean     array IsAnySlotSelected
        boolean     array IsInventoryOpened
        integer     array pRow
        integer     array pCol
        integer     array prevTrackId
        integer     array globalUnitId
        camerasetup array inventoryCamera
        camerasetup array initialCamera
    endglobals



    function itemDatabase takes nothing returns nothing
        local integer i
        set i = 1
        set itemType[i] = i
        set itemIcon[i] = 'Z000'
        set groundItemId[i] = 'P000'
        set itemName[i] = "Shaman Claw"
        set itemDescription[i] = ""
        set itemEffect[i] = null
        call SaveInteger( Data , itemTableKey , groundItemId[i] , i )

        set i = i + 1
        set itemType[i] = i
        set itemIcon[i] = 'Z001'
        set groundItemId[i] = 'P001'
        set itemName[i] = "Amulet"
        set itemDescription[i] = ""
        set itemEffect[i] = null
        call SaveInteger( Data , itemTableKey , groundItemId[i] , i )

        set i = i + 1
        set itemType[i] = i
        set itemIcon[i] = 'Z002'
        set groundItemId[i] = 'P002'
        set itemName[i] = "Orb of Fire"
        set itemDescription[i] = ""
        set itemEffect[i] = null
        call SaveInteger( Data , itemTableKey , groundItemId[i] , i )

        set i = i + 1
        set itemType[i] = i
        set itemIcon[i] = 'Z003'
        set groundItemId[i] = 'P003'
        set itemName[i] = "Orb of Frost"
        set itemDescription[i] = ""
        set itemEffect[i] = null
        call SaveInteger( Data , itemTableKey , groundItemId[i] , i )

        set MAX_ITEMS = i
    endfunction



    function showInterface takes integer itemId , integer slotId returns nothing
        local destructable d = LoadDestructableHandle( Data , itemId , slotId )
        call ShowDestructable( d , true )
    endfunction



    function hideInterface takes integer itemId , integer slotId returns nothing
        local destructable d = LoadDestructableHandle( Data , itemId , slotId )
        if itemId != -1 then
            call ShowDestructable( d , false )
        endif
    endfunction



    function initiateInventoryForUnit takes unit whichUnit returns nothing
        local integer r
        local integer c
        local integer trackId
        local integer unitId = GetHandleId(whichUnit)
        local player p = GetOwningPlayer(whichUnit)
        local integer pid = GetPlayerId(p)

        call UnitAddAbility( whichUnit , INV_ABILITY_ID )

            set r = 1
            loop
                exitwhen r > MAX_ROW_COUNT
                set c = 1
                loop
                    exitwhen c > MAX_COL_COUNT
                    set trackId = LoadInteger( Data , -pid - trackKey , r * MAX_SIZE + c )
                    call SaveInteger( Data , unitId , trackId , -1 )
                    set c = c + 1
                endloop
                set r = r + 1
            endloop

    endfunction



    function loadInventoryForUnit takes unit whichUnit returns nothing
        local integer r
        local integer c
        local integer trackId
        local integer itemId
        local integer unitId = GetHandleId(whichUnit)
        local player p = GetOwningPlayer(whichUnit)
        local integer pid = GetPlayerId(p)

            set r = 1
            loop
                exitwhen r > MAX_ROW_COUNT
                set c = 1
                loop
                    exitwhen c > MAX_COL_COUNT
                    set trackId = LoadInteger( Data , -pid - trackKey , r * MAX_SIZE + c )
                    set itemId = LoadInteger( Data , unitId , trackId )
                    call showInterface( itemId , trackId )
                    set c = c + 1
                endloop
                set r = r + 1
            endloop

    endfunction



    function onClick_Actions takes nothing returns nothing
        local integer unitId
        local integer trackId
        local integer pid
        local trackable trigTrack
        local player trigPlayer
        local integer row
        local integer col
        local integer previousItemId
        local integer currentItemId
        local destructable selector

        set trigPlayer = GetLocalPlayer()
        set pid        = GetPlayerId( trigPlayer )
        set trigTrack  = GetTriggeringTrackable()
        set trackId    = GetHandleId( trigTrack )
        set unitId     = globalUnitId[pid]
        set row        = LoadInteger( Data , -pid - rowKey , trackId )
        set col        = LoadInteger( Data , -pid - colKey , trackId )

        if IsAnySlotSelected[pid] == false then
            set pRow[pid] = row
            set pCol[pid] = col
            set prevTrackId[pid] = trackId
            // Show Selector
            set selector = LoadDestructableHandle( Data , -2 , trackId )
            call ShowDestructable( selector , true )
            set IsAnySlotSelected[pid] = true
        else
            // Get item Id in slot pRow,pCol
            set previousItemId = LoadInteger( Data , unitId , prevTrackId[pid] )
            // Get item Id in slot r,c
            set currentItemId  = LoadInteger( Data , unitId , trackId )
            // swap slot item ids data
            if previousItemId != currentItemId and previousItemId != -1 then
                call SaveInteger( Data , unitId , trackId          , previousItemId )
                call SaveInteger( Data , unitId , prevTrackId[pid] , currentItemId  )
                // Showing and Hiding Interfaces
                call showInterface(previousItemId,trackId)
                call showInterface(currentItemId,prevTrackId[pid])
                call hideInterface(previousItemId,prevTrackId[pid])
                call hideInterface(currentItemId,trackId)
            endif

            // Hide Selector
            set selector = LoadDestructableHandle( Data , -2 , prevTrackId[pid] )
            call ShowDestructable( selector , false )
            set IsAnySlotSelected[pid] = false
        endif
    endfunction



    function onTrack_Actions takes nothing returns nothing
    endfunction



    function onOpen_Actions takes nothing returns nothing
        local unit inventoryUnit = GetTriggerUnit()
        local integer unitId = GetHandleId( inventoryUnit )
        local player p = GetOwningPlayer( inventoryUnit )
        local integer pid = GetPlayerId( p )

        if ( GetLocalPlayer() == p ) then
            set globalUnitId[pid] = unitId // a global variable that will be used in the 'onClick' and 'onTrack' function
            if IsInventoryOpened[pid] == false then
                set IsInventoryOpened[pid] = true
                set initialCamera[pid] = GetCurrentCameraSetup()
                call CameraSetupApplyForceDuration(inventoryCamera[pid] , true , 0 )
                call loadInventoryForUnit( inventoryUnit )
            else
                set IsInventoryOpened[pid] = false
                call CameraSetupApplyForceDuration(initialCamera[pid] , true , 0 )
            endif
        endif
    endfunction



    function fullInventory takes integer powerupId , unit aquiringUnit , integer pid returns nothing
        local real x = GetUnitX( aquiringUnit )
        local real y = GetUnitY( aquiringUnit )
        call CreateItem(powerupId,x,y)
        call SimError(Player(pid) , "Backpack Inventory is Full" )
    endfunction



    function getNextEmptySlot takes integer pid , integer unitId returns integer
        local integer r
        local integer c
        local integer trackId
        local integer itemId

        set r = 1
        loop
            exitwhen r > MAX_ROW_COUNT
            set c = 1
            loop
                exitwhen c > MAX_COL_COUNT

                set trackId = LoadInteger( Data , -pid - trackKey , r * MAX_SIZE + c )
                set itemId  = LoadInteger( Data , unitId , trackId )

                if itemId == -1 then
                    return trackId
                endif

                set c = c + 1
            endloop
            set r = r + 1
        endloop

        return -1
    endfunction



    function onAquire_Actions takes nothing returns nothing
        local item aquiredItem = GetManipulatedItem()
        local integer powerupId = GetItemTypeId( aquiredItem )
        local integer i = LoadInteger( Data , itemTableKey , powerupId )
        local unit aquiringUnit = GetTriggerUnit()
        local integer unitId = GetHandleId(aquiringUnit)
        local player p = GetOwningPlayer(aquiringUnit)
        local integer pid = GetPlayerId(p)
        local integer emptySlotId

        set emptySlotId = getNextEmptySlot( pid , unitId)
        if emptySlotId != -1 then
            call SaveInteger( Data , unitId , emptySlotId , itemType[i] )
            call showInterface( itemType[i] , emptySlotId )
        else
            call fullInventory( powerupId , aquiringUnit , pid )
        endif
    endfunction



    function baseLayoutConstruction takes nothing returns nothing
        local integer pid
        local real x1
        local real y1
        local real x
        local real y
        local integer r
        local integer c
        local integer i
        local integer track_id
        local trackable slotBase
        local destructable temp_destruct
        local camerasetup temp_cam

        call BJDebugMsg(R2S(H)) // Displays the value that will be applied to Camera Field: Target Distance
        set pid = 0
        loop
            exitwhen pid >= PLAYER_INGAME_COUNT
            set x1 = BASE_X + pid * (( MAX_COL_COUNT * OFFSET_X) + BORDER_SIZE)
            set y1 = BASE_Y //- pid * (( MAX_ROW_COUNT * OFFSET_Y) + BORDER_SIZE)

            // Creating camera setups
            set x = x1 + (( OFFSET_X * ( MAX_COL_COUNT - 1 ) )/2)
            set y = y1 - (( OFFSET_Y * ( MAX_ROW_COUNT - 1 ) )/2)

            set temp_cam = CreateCameraSetup()
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_ZOFFSET        ,   0.0 , 0.0)
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_ROTATION       ,  90.0 , 0.0)
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_ANGLE_OF_ATTACK, 270.0 , 0.0)
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_TARGET_DISTANCE,  1500/*H*/    , 0.0)
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_ROLL           ,   0.0 , 0.0)
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_FIELD_OF_VIEW  , 120.0 , 0.0)
            call CameraSetupSetField( temp_cam, CAMERA_FIELD_FARZ           , 5000.0, 0.0)
            call CameraSetupSetDestPosition(temp_cam, x , y , 0.0)
            set inventoryCamera[pid] = temp_cam

            set r = 1
            loop
                exitwhen r > MAX_ROW_COUNT
                set c = 1
                loop
                    exitwhen c > MAX_COL_COUNT
                        set x = x1 + ( OFFSET_X * ( c - 1 ) )
                        set y = y1 - ( OFFSET_Y * ( r - 1 ) )
                        set slotBase = CreateTrackable( TRACK_MODEL , x , y , 0 )
                        set track_id = GetHandleId( slotBase )

                        //call TriggerSleepAction(0.0)
                        set BIG_MSG = "Player_ID: |cffFFCC00" + I2S(pid) +/*
                        */ "|r, Row#: |cffFFCC00" + I2S(r) +/*
                        */ "|r, Col#: |cffFFCC00" + I2S(c) +/*
                        */ "|r,Trackable_ID: |cffFFCC00" + I2S(track_id) + "|r"
                        call BJDebugMsg(BIG_MSG)

                        call SaveInteger( Data , -pid - rowKey  , track_id , r)
                        call SaveInteger( Data , -pid - colKey  , track_id , c)
                        call SaveInteger( Data , -pid - trackKey , r * MAX_SIZE + c , track_id )
                        call TriggerRegisterTrackableHitEvent  ( onClick , slotBase )
                        call TriggerRegisterTrackableTrackEvent( onTrack , slotBase )
                        set temp_destruct = CreateDestructable( BASE_MODEL_ID , x , y , 0 , 1 , 0 )
                        call SaveDestructableHandle( Data , -1 , track_id , temp_destruct )
                        set temp_destruct = CreateDestructable( SELECTOR_MODEL_ID , x , y , 0 , 1 , 0 )
                        call SaveDestructableHandle( Data , -2 , track_id , temp_destruct )
                        call ShowDestructable( temp_destruct , false )
                        set i = 1
                        loop // Creating the dummy destructables for all available items
                            exitwhen i > MAX_ITEMS
                            set temp_destruct = CreateDestructable( itemIcon[i] , x , y , 0 , 1 , 0 )
                            call SaveDestructableHandle( Data , itemType[i] , track_id , temp_destruct )
                            call ShowDestructable( temp_destruct , false )
                            set i = i + 1
                        endloop
                    set c = c + 1
                    //call BJDebugMsg(I2S(c))
                endloop
                set r = r + 1
                //call BJDebugMsg(I2S(r))
            endloop
            set pid = pid + 1
        endloop

    endfunction



    function misc takes nothing returns nothing
        call FogModifierStart(CreateFogModifierRect(Player(0),FOG_OF_WAR_VISIBLE,bj_mapInitialPlayableArea,true,false))
    endfunction



function actn takes nothing returns nothing
    if ( GetUnitTypeId(GetFilterUnit()) == 'Hpal' ) then
        call initiateInventoryForUnit( GetEnumUnit() )
    endif
endfunction

function init_units takes nothing returns nothing
    set bj_wantDestroyGroup = true
    call ForGroupBJ( GetUnitsInRectMatching(GetPlayableMapRect(), null), function actn )
endfunction



    function InitTrig_FSI takes nothing returns nothing
        call TriggerRegisterAnyUnitEventBJ( onOpen, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerRegisterAnyUnitEventBJ( onAquire , EVENT_PLAYER_UNIT_PICKUP_ITEM )
        call TriggerAddAction( onClick  , function onClick_Actions  )
        call TriggerAddAction( onTrack  , function onTrack_Actions  )
        call TriggerAddAction( onOpen   , function onOpen_Actions   )
        call TriggerAddAction( onAquire , function onAquire_Actions )
        call misc()
        call itemDatabase()
        call baseLayoutConstruction()

        // initiate an empty inventory for an example unit
        //call initiateInventoryForUnit( gg_unit_Hpal_0012 )
        call init_units()

    endfunction
 

Attachments

  • Trackables.w3x
    32.8 KB · Views: 115
Last edited:
Level 16
Joined
Mar 3, 2006
Messages
1,564
That is a fair amount of code. It will exit early if the thread crashes, i.e. you do something bad in a function.

Which exact functions exit early?

baseLayoutConstruction, I knew it was this through the debug message I added: I am constructing 25x25 Inventory but it exit at 16x18 (data are Row x Col), I know it is a very big inventory but all I am doing foe the sake of learning. Any help ??
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
there is a thing called the operation limit. When you do too many operations in a thread, the thread will terminate. You avoid this by splitting the operations into multiple asynchronous threads using trigger evaluations.

See my resource BigInt in the JASS section for an example of this.

Its too complicated for me to understand, how do I use it in my code ?
 
I normally use ForForce to start a new thread, just for its easiness. But anyway, this is how you would start a new thread.

Original, WITHOUT starting a new thread:
JASS:
function NormalFunction takes nothing returns nothing
    // actions
endfunction
This is the setup for starting a NEW thread:
JASS:
function ChildFunction takes nothing returns nothing
    // actions
endfunction

function ParentFunction takes nothing returns nothing
    call ForForce(bj_FORCE_PLAYER[0], function ChildFunction)
endfunction

Starting a new thread will refresh the op limit. It will still follow the rules of the op limit, but by splitting up your code you can avoid hitting it.

If you need to pass values, you can use globals. Take for example this case:
JASS:
function Example takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local group g = CreateGroup()
    local location l = Location(0, 0)
    // some actions 
    // actions using those locals that you want to put in a new thread
endfunction
That is the original. If you want to start a new thread and the actions you are going to move require those locals, then I recommend that you either use temporary globals or just use globals all together instead:
JASS:
globals
    // NOTE: I recommend that you put this code into
    //          a scope or library and make these variables
    //          private to prevent name collision.
    unit u
    real x
    real y
    group g
    location l 
endglobals

function Example_Child takes nothing returns nothing
    // actions
endfunction

function Example takes nothing returns nothing
    set u = GetTriggerUnit()
    set x = GetUnitX(u)
    set y = GetUnitY(u)
    set g = CreateGroup()
    set l = Location(0, 0)
    // some actions
    call ForForce(bj_FORCE_PLAYER[0], function Example_Child)
endfunction

Yeah, it is a bit tedious, but it works.

As for your code, there is a loop within a loop within a loop within a loop, so it most likely is hitting the op limit. So the question is, where should you start the new thread? It depends, but I would start it around here:
JASS:
    loop
            exitwhen r > MAX_ROW_COUNT
            set c = 1
            // another loop of actions
            // ...
    endloop
Move the part that is commented as // another loop of actions into a separate function and call it on a new thread. Use the techniques I talked about above (with globals instead of locals) and then it should (hopefully) work. If the op limit is still being hit, then you have to separate it into even more functions.
 
Status
Not open for further replies.
Top