HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Rally Point System || Need ideas.

08-18-2007, 03:21 AM#1
Jazradel
I just rewrote a old rally points system to use structs and I'd like to see if I can finally get it polished.

Hidden information:
Useful common functions.
Collapse JASS:
library Functions initializer Init
globals
    gamecache cache = InitGameCache("Cache")
    boolexpr True
endglobals

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function FlushCache takes handle subject returns nothing
    call FlushStoredMission(cache, I2S(H2I(subject)) )
endfunction

function SetCacheInt takes handle subject, string name, integer value returns nothing
    call StoreInteger(cache, I2S(H2I(subject)), name, value)
endfunction

function GetCacheInt takes handle subject, string name returns integer
    return GetStoredInteger(cache, I2S(H2I(subject)), name)
endfunction

function TrueFunc takes nothing returns boolean
    return true
endfunction

function Init takes nothing returns nothing
    set True = Condition(function TrueFunc)
endfunction

function SimError takes player ForPlayer, string msg returns nothing
    if (GetLocalPlayer() == ForPlayer) then
        if (msg!="") and (msg!=null) then
            call ClearTextMessages()
            call DisplayTimedTextToPlayer( ForPlayer, 0.52, -1.00, 2.00, "|cffffcc00"+msg+"|r" )
        endif
    endif
endfunction
endlibrary
The actual system.
Collapse JASS:
library RallyPointSystem needs Functions
globals
    integer ORPId = 'ARal'
    integer IntLimit = 10 //276447232 Maximum value an integer can support
    //But in this case the maximum number that can exists in the structs array
endglobals

struct crp
    trigger train
    trigger spell
    trigger select
    
    integer flagno
    unit array flagunit[10]
    trigger array flagtrig[10]
    real array flagx[10]
    real array flagy[10]
    lightning array flaglight[10]
    
    integer range = 70
    integer flagcode
    string order = "attack"

    string ltype
    integer red = 1
    integer blue = 1
    integer green = 1
    integer alpha = 1
    
    integer limit = IntLimit
    integer tlimit = IntLimit
    integer slimit = IntLimit
    integer rlimit = IntLimit
endstruct

struct crpu
    unit origin
    integer limit
    integer lastflag = 1
endstruct

struct crpt
    unit origin
    integer flagno
endstruct

//===========================================================================

function RemoveCRP takes unit u returns nothing
    local integer i = 0
    local crp c = GetCacheInt(u, "CRP")
    loop
        set i = i + 1
        exitwhen i > c.flagno
        call RemoveUnit(c.flagunit[i])
        call DestroyTrigger(c.flagtrig[i])
        call DestroyLightning(c.flaglight[i])
    endloop
    call DestroyTrigger(c.train)
    call DestroyTrigger(c.spell)
    call DestroyTrigger(c.select)
    call UnitAddAbility(u, ORPId)
    call c.destroy()
endfunction

function MoveToNextFlag takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local trigger t = GetTriggeringTrigger()
    local crpu cu = GetCacheInt(u,"CRP") 
    local crpt ct = GetCacheInt(t,"CRP")
    local crp c = GetCacheInt(cu.origin,"CRP")
    if cu.lastflag < c.flagno and ct.flagno < cu.limit then
        if cu.lastflag == ct.flagno and cu.origin == ct.origin then
            set cu.lastflag = cu.lastflag + 1
            call IssuePointOrder(u, c.order, c.flagx[cu.lastflag], c.flagy[cu.lastflag])
        endif
    else
        call cu.destroy()
        call FlushCache(u)
    endif
    set u = null
    set t = null
endfunction

function MoveToFirstFlag takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local unit m
    local crp c = GetCacheInt(u, "CRP")
    local crpu cu
    if c.limit != 0 and c.flagno > 0 then
        set cu = crpu.create()
        if GetTrainedUnit() != null then
            set m = GetTrainedUnit()
            set cu.limit = c.tlimit
        elseif GetSoldUnit() != null then
            set m = GetSoldUnit()
            set cu.limit = c.slimit
        elseif GetRevivingUnit() != null then
            set m = GetRevivingUnit()
            set cu.limit = c.rlimit
        endif
        set cu.origin = u
        call SetCacheInt(m, "CRP", cu)
        call IssuePointOrder(m, c.order, c.flagx[1], c.flagy[1])
    endif
    set u = null
    set m = null
endfunction

function AddRallyPoint takes unit u, real x, real y returns integer
    local integer i = 0
    local crp c = GetCacheInt(u, "CRP")
    local crpt ct
    local fogmodifier v
    if c.flagno < c.limit then
        set c.flagno = c.flagno + 1
        set c.flagx[c.flagno] = x
        set c.flagy[c.flagno] = y
        if IsVisibleToPlayer(c.flagx[c.flagno], c.flagy[c.flagno], GetOwningPlayer(u)) == false then
            call SimError(GetOwningPlayer(u), "Effects will not display properly outside your line of sight")
        endif
        set v = CreateFogModifierRadius(GetOwningPlayer(u),FOG_OF_WAR_VISIBLE, c.flagx[c.flagno], c.flagy[c.flagno], 128, false, false)
        call FogModifierStart(v)
        set c.flagtrig[c.flagno] = CreateTrigger()
        call TriggerAddAction(c.flagtrig[c.flagno], function MoveToNextFlag)
        set ct = crpt.create()
        set ct.origin = u
        set ct.flagno = c.flagno
        call SetCacheInt(c.flagtrig[c.flagno],"CRP",ct)
        set c.flagunit[c.flagno] = CreateUnit(GetOwningPlayer(u), c.flagcode, c.flagx[c.flagno],  c.flagy[c.flagno], 0)        
        call TriggerRegisterUnitInRange(c.flagtrig[c.flagno], c.flagunit[c.flagno], c.range, True)
        if GetLocalPlayer() == GetOwningPlayer(u) then
            if c.flagno > 1 then
                set c.flaglight[c.flagno] = AddLightning(c.ltype, true, c.flagx[c.flagno-1], c.flagy[c.flagno-1], c.flagx[c.flagno], c.flagy[c.flagno])
            else
                set c.flaglight[c.flagno] = AddLightning(c.ltype, true, GetUnitX(u), GetUnitY(u), c.flagx[c.flagno], c.flagy[c.flagno])
            endif
            call SetLightningColor( c.flaglight[c.flagno], c.red, c.green, c.blue, c.alpha)
        else
            call SetUnitVertexColor( c.flagunit[c.flagno], 255, 255, 255, 0)
        endif
        call DestroyFogModifier(v)
    endif
    return c.flagno
endfunction

function RemoveLastRallyPoint takes unit u returns nothing
    local crp c = GetCacheInt(u, "CRP")
    if c.flagno > 0 then
        call DestroyTrigger(c.flagtrig[c.flagno])
        call RemoveUnit(c.flagunit[c.flagno])
        call DestroyLightning(c.flaglight[c.flagno])
        set c.flagno = c.flagno - 1
    endif
    set u = null
endfunction

function ClearRallyPoint takes unit u returns nothing
    local integer i = 0
    local crp c = GetCacheInt(u, "CRP")
    loop
        set i = i + 1
        exitwhen i > c.flagno
        call RemoveUnit(c.flagunit[i])
        call DestroyTrigger(c.flagtrig[i])
        call DestroyLightning(c.flaglight[i])
    endloop
    set c.flagno = 0
    set u = null
endfunction

function Select takes nothing returns nothing
    local integer i = 0
    local unit u = GetTriggerUnit()
    local crp c = GetCacheInt(u, "CRP")
    if GetTriggerEventId() == EVENT_UNIT_SELECTED then
        loop
            set i = i + 1
            exitwhen i > c.flagno
            if GetLocalPlayer() == GetOwningPlayer(u) then
                call SetLightningColor( c.flaglight[i], c.red, c.green, c.blue, c.alpha)
                call SetUnitVertexColor( c.flagunit[i], 255, 255, 255, 255)
            endif
        endloop
    elseif GetTriggerEventId() == EVENT_UNIT_DESELECTED then
        loop
            set i = i + 1
            exitwhen i > c.flagno
            call SetUnitVertexColor( c.flagunit[i], 255, 255, 255, 0)
            call SetLightningColor( c.flaglight[i], 255, 255, 255, 0)
        endloop
    endif
    set u = null
endfunction
    
function RegisterCRP takes unit u,integer flag, string ltype returns nothing
    local trigger train = CreateTrigger()
    local trigger spell = CreateTrigger()
    local trigger select = CreateTrigger()
    local crp c = crp.create()
    //Order units when trained
    call TriggerAddAction(train, function MoveToFirstFlag)
    call TriggerRegisterUnitEvent( train, u, EVENT_UNIT_SELL )
    call TriggerRegisterUnitEvent( train, u, EVENT_UNIT_TRAIN_FINISH )
    call TriggerRegisterUnitEvent( train, u, EVENT_UNIT_HERO_REVIVE_FINISH )
    //Show points when selected
    call TriggerAddAction(select, function Select)
    call TriggerRegisterUnitEvent( select, u, EVENT_UNIT_SELECTED )
    call TriggerRegisterUnitEvent( select, u, EVENT_UNIT_DESELECTED )
    //Add abilites
    call UnitRemoveAbility(u, ORPId)
    //Set crp
    set c.train = train
    set c.spell = spell
    set c.select = select
    set c.flagcode = flag
    set c.ltype = ltype
    call SetCacheInt(u, "CRP", c)
    //Cleanup
    set train = null
    set spell = null
    set select = null
endfunction
endlibrary
Advanced commands for configuration.
Collapse JASS:
library RallyPointCommands needs RallyPointSystem
function SetFlagRange takes unit u, integer range returns nothing
    local crp c = GetCacheInt(u, "CRP")
    set c.range = range
endfunction

function SetCRPOrder takes unit u, string order returns nothing
    local crp c = GetCacheInt(u, "CRP")
    set c.order = order
endfunction

function SetFlagCode takes unit u, integer model returns nothing
    local crp c = GetCacheInt(u, "CRP")
    local group g = CreateGroup()
    call GroupEnumUnitsSelected(g, GetOwningPlayer(u), True)
    set c.flagcode = model
    if IsUnitInGroup(u, g) and GetLocalPlayer() == GetOwningPlayer(u) then
        call SelectUnit(u, false)
        call SelectUnit(u, true)
    endif
    call DestroyGroup(g)
    set g = null
endfunction

function SetLightningType takes unit u, string ltype returns nothing
    local crp c = GetCacheInt(u, "CRP")
    local group g = CreateGroup()
    call GroupEnumUnitsSelected(g, GetOwningPlayer(u), True)
    set c.ltype = ltype
    if IsUnitInGroup(u, g) and GetLocalPlayer() == GetOwningPlayer(u) then
        call SelectUnit(u, false)
        call SelectUnit(u, true)
    endif
    call DestroyGroup(g)
    set g = null
endfunction

function SetLightningShade takes unit u, integer red, integer green, integer blue, integer alpha returns nothing
    local crp c = GetCacheInt(u,"CRP")
    local group g = CreateGroup()
    call GroupEnumUnitsSelected(g, GetOwningPlayer(u), True)
    set c.red = red
    set c.green = green
    set c.blue = blue
    set c.alpha = alpha
    if IsUnitInGroup(u, g) and GetLocalPlayer() == GetOwningPlayer(u) then
        call SelectUnit(u, false)
        call SelectUnit(u, true)
    endif
    call DestroyGroup(g)
    set g = null
endfunction

function SetMaxPoints takes unit u, integer max returns nothing
    local crp c = GetCacheInt(u,"CRP")
    local integer i
    if max == -1 then
        set max = IntLimit
    endif
    set c.limit = max
    if max >  c.flagno then
        set i = c.flagno
        loop
            set i = i + 1
            exitwhen i > max
            call RemoveUnit(c.flagunit[i])
            call DestroyLightning(c.flaglight[i])
        endloop
        set c.flagno = max
    endif
endfunction

function SetEventLimit takes unit u, event etype, integer max returns nothing
    local crp c = GetCacheInt(u,"CRP")
    if max == -1 then
        set max = IntLimit
    endif
    if etype == EVENT_UNIT_TRAIN_FINISH then
        set c.tlimit = max
    elseif etype == EVENT_UNIT_SELL then
       set c.slimit = max
    elseif etype == EVENT_UNIT_HERO_REVIVE_FINISH then
        set c.rlimit = max
    endif
endfunction

function RemoveRallyPoint takes unit u, integer i returns nothing
    local crp c = GetCacheInt(u, "CRP")
    local integer n = i
    if i > 0 and i < c.flagno  then
        call DestroyTrigger(c.flagtrig[i])
        call RemoveUnit(c.flagunit[i])
        call DestroyLightning(c.flaglight[i])
        loop
            exitwhen n > c.flagno
            set c.flagtrig[n] = c.flagtrig[n+1]
            set c.flagunit[n] = c.flagunit[n+1]
            set c.flaglight[n] = c.flaglight[n+1]
            set n = n + 1
        endloop
        set c.flagno = c.flagno - 1
    endif
    set u = null
endfunction
endlibrary


It's working fine (haven't tested multiplayer/advanced commands yet though) but I've run into several problems I can't figure out how to solve:
  • Particles on the flags still appear when they are hidden because SetUnitVertexColor doesn't work completely. I could change the flags to effects, but that would add extra overhead I don't want.
  • The flags doesn't appear when they are out of your line of sight. I've tried them as units and effects and neither works correctly.
  • Because I'm using an array inside a struct, the user is limited by the maximum number vs. the units with rally points.
  • I have no idea how to properly clean up structs attached to units currently moving between flags when the flags are destroyed. I could use a lot of collections, but does anyone have a better idea?

Does anyone have any idea on how to solve these?
Attached Files
File type: w3xRally Point System.w3x (25.3 KB)
08-18-2007, 10:28 AM#2
cohadar
You can solve your problem (and simplify your system)
by using collections.

You don't need to attach structs to units that way.

All you need to do is attach one collection
to every flag that will contain all the units that are moving
towards that flag.
When unit reaches the flag you transfer it from that flags collection
to the next one.

Piece of cake.

I recommend this as usual:
http://www.wc3campaigns.net/showthread.php?t=96112

EDIT: after looking some further I concluded that it would be actually better to have only
one collection for the whole game
Collapse JASS:
struct RallyData
   unit u   // unit that is moving
   unit nextFlag
   integer flagCount // current flag number (in order of flag creation)
endstruct

If the unit dies or is issued different order while on rally path you simply
remove it from the global collection.