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

Arcing TextSplat v1.2.1

Analogous to Arcing Floating Text this system creates floating texts over coordinates.
The difference between the two is, that "Arcing TextSplat" uses the textsplat object instead of the texttag handle.
Unlike the texttag library Arcing TextSplat does not increase the text size,
because the periodic operation is too heavy for textsplats and would make the system useless.

Arcing TextSplat is slower in performance that Arcing Floating Text and should only be used if:

  • Your map is about to hit the maximum texttag handle counter ( 100 ).
  • You want to bind in images into the text ( |ipath|i ).
  • You wish to use a custom font for your map.
  • You have a liking for the image handle.

API
function CreateArcingTextSplat takes string s, real x, real y returns nothing
  • Creates a arcing textsplat on coordinates x/y.
function CreateArcingTextSplatForForce takes string s, real x, real y, force forForce returns nothing
  • For local traffic code. Is only displayed for force "forForce".

Obligatory requirements

JASS:
library ArcingTextSplat uses TextSplat2//* v1.2.1
//**
//*  Performance:
//*  ============
    //*  The creation of textsplat objects is a bit expensive, hence 
    //* ArcingTextSplat is slower than the similar library Arcing Floating Text,
    //* which uses the texttag handle. Use ArcingTextSplat only if:
    //*     - Your map hits the maximum texttag count of 100.
    //*     - You want to bind in images into the text ( |ipath|i ).
    //*     - You have a liking for the image handle. 
//**
//* Import instruction:
//* ===================
    //*  Copy ArcingTextSplat, TextSplat2, Font and their requirements into your map.
    //* Install a custom font of your choice. For more information look into library Font.
//**
//*  API: 
//*  ====
    //! novjass
        function CreateArcingTextSplat takes string s, real x, real y returns nothing
        function CreateArcingTextSplatForForce takes string s, real x, real y, force forForce returns nothing
        //*  For local traffic code. Is only displayed for force "forForce".
    //! endnovjass
//**
//*  User settings:
//*  ==============
    globals
        private constant real TEXTSIZE        = 8       //*  Set the size of the text.
        private constant real TIME_LIFE       = 1.0     //*  Set how long the text lasts.
        private constant real TIME_FADE       = 0.5     //*  Set when the text starts to fade.
        private constant real Z_OFFSET        = 50      //*  Set the height above the unit.
        private constant real Z_OFFSET_BON    = 70      //*  Set how much extra height the text gains.
        private constant real VELOCITY        = 2       //*  Set how fast the text move in x/y plane.
        private constant real FIX_ANGLE       = bj_PI/2 //*  Set the movement angle of the text. Does not apply if ANGLE_RND is true.
        private constant boolean ANGLE_RND    = true    //*  Set if the angle random or fixed.
    endglobals
//========================================================================
//*  Arcing TextSplat system code. Make changes carefully.
//======================================================================== 
    globals
        private constant timer TMR = CreateTimer()
        //*  Indexing.
        private integer max = 0
        //*  Array.
        private textsplat array splat
    endglobals
 
    private function OnPeriodic takes nothing returns nothing
        local integer dex = 0
        local textsplat t
        loop
            exitwhen (dex == max)
            set t = splat[dex]
            if (t.lifespan - t.age > 0.) then
                call t.setPosition(t.x, t.y, Z_OFFSET + Z_OFFSET_BON*Sin(bj_PI*(t.lifespan - t.age))) 
            else
                set max = max - 1
                set splat[dex] = splat[max]
                set dex = dex - 1
                if (0 == max) then
                    call PauseTimer(TMR)
                endif
            endif
            set dex = dex + 1
        endloop    
    endfunction
    
    function CreateArcingTextSplatForForce takes string s, font f, real x, real y, force forForce returns nothing
        static if ANGLE_RND then
            local real a = GetRandomReal(-bj_PI, bj_PI)
        else 
            local real a = FIX_ANGLE
        endif
        //*  textsplat API.
        local textsplat t = textsplat.create(f)
        set t.lifespan = TIME_LIFE
        set t.fadepoint = TIME_FADE
        set t.permanent = false
        call t.setText(s, TEXTSIZE, TEXTSPLAT_TEXT_ALIGN_CENTER)
        call t.setPosition(x, y, Z_OFFSET)
        call t.setVelocity(Sin(a)*VELOCITY*32, Cos(a)*VELOCITY*32, 0.)
        call t.setVisible(IsVisibleToPlayer(x, y, GetLocalPlayer()) and IsPlayerInForce(GetLocalPlayer(), forForce))
        //*  Set members.
        set splat[max] = t
        //*  Start timer.
        if (0 == max) then
            call TimerStart(TMR, .031250000, true, function OnPeriodic)
        endif
        set max = max + 1
    endfunction
    
    function CreateArcingTextSplat takes string s, font f, real x, real y returns nothing
        call CreateArcingTextSplatForForce(s, f, x, y, bj_FORCE_ALL_PLAYERS)
    endfunction

endlibrary

Keywords:
floating, textsplat, text
Contents

Just another Warcraft III map (Map)

Reviews
20:07, 9th Jan 2016 IcemanBo: Nice short snippet that uses TextSplat and makes them arcing.

Moderator

M

Moderator

20:07, 9th Jan 2016
IcemanBo: Nice short snippet that uses TextSplat and makes them arcing.
 
This seems really cool, but I probably would prefer having x/y parameter instead of unit.
You could create these then with certain offsets, or just also des destructables, items, etc.
(since it is anyways not limited to damage only, but can be all text)

Only as side note, I would use dynamic indexing here, but it's mostly personal preference.
The order of instance execution does not matter at all here, because it's only about text displaying,
so I personly would just make an extra list[] instead of caring much about alloc/dealloc properly.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I changed the unit argument to coordinates x/y.
Also changed to dynamic indexing. That was a good idea, because I could remove.
Also remembered that I can read a textsplat position, hence I remove posX/posY.

Overall only positive changes. :)

I've placed a few enemy footmans in the right upper corner.
You can see that textsplats are not shown for you, when the coordinates are in fog.

Edit:
x/y velocity is now handled internally by textsplat, so I could remove the cos/sin array.
time tracking is calculated by splat.lifespan - text.age, so I removed the time array.

Edit2:
Optimized the code a bit.
 
Last edited:
Cool. ^^

You can see that textsplats are shown for you, when the coordinates are in fog.
You mean "can not"? Because I only see when I move up with hero. Or I'm wrong with something maybe (?)

And just as note. The new demo that was added doesn't work properly,
because once the units are hidden they won't be picked anymore by "PickUnitsInRect" function.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You mean "can not"? Because I only see when I move up with hero. Or I'm wrong with something maybe (?)
typo from me. :)

Splats are hidden if a player has fog of war on the coordinates.

call t.setVisible(IsVisibleToPlayer(x, y, GetLocalPlayer()) and IsPlayerInForce(GetLocalPlayer(), forForce))

And just as note. The new demo that was added doesn't work properly,
because once the units are hidden they won't be picked anymore by "PickUnitsInRect" function.
I don't understand. The library does nothing related to unit handles at all.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
why can I only make it global or for certain force? what if I want to show it only to one player?
There is bj_FORCE_PLAYER[x] in the blizzard j, which is initialized here
JASS:
function InitBlizzardGlobals takes nothing returns nothing
    local integer index
    local integer userControlledPlayers
    local version v

    // Init filter function vars
    set filterIssueHauntOrderAtLocBJ = Filter(function IssueHauntOrderAtLocBJFilter)
    set filterEnumDestructablesInCircleBJ = Filter(function EnumDestructablesInCircleBJFilter)
    set filterGetUnitsInRectOfPlayer = Filter(function GetUnitsInRectOfPlayerFilter)
    set filterGetUnitsOfTypeIdAll = Filter(function GetUnitsOfTypeIdAllFilter)
    set filterGetUnitsOfPlayerAndTypeId = Filter(function GetUnitsOfPlayerAndTypeIdFilter)
    set filterMeleeTrainedUnitIsHeroBJ = Filter(function MeleeTrainedUnitIsHeroBJFilter)
    set filterLivingPlayerUnitsOfTypeId = Filter(function LivingPlayerUnitsOfTypeIdFilter)

    // Init force presets
    set index = 0
    loop
        exitwhen index == bj_MAX_PLAYER_SLOTS
        set bj_FORCE_PLAYER[index] = CreateForce()
        call ForceAddPlayer(bj_FORCE_PLAYER[index], Player(index))
        set index = index + 1
    endloop

    set bj_FORCE_ALL_PLAYERS = CreateForce()
    call ForceEnumPlayers(bj_FORCE_ALL_PLAYERS, null)

    // Init Cinematic Mode history
    set bj_cineModePriorSpeed = GetGameSpeed()
    set bj_cineModePriorFogSetting = IsFogEnabled()
    set bj_cineModePriorMaskSetting = IsFogMaskEnabled()

    // Init Trigger Queue
    set index = 0
    loop
        exitwhen index >= bj_MAX_QUEUED_TRIGGERS
        set bj_queuedExecTriggers[index] = null
        set bj_queuedExecUseConds[index] = false
        set index = index + 1
    endloop

    // Init singleplayer check
    set bj_isSinglePlayer = false
    set userControlledPlayers = 0
    set index = 0
    loop
        exitwhen index >= bj_MAX_PLAYERS
        if (GetPlayerController(Player(index)) == MAP_CONTROL_USER and GetPlayerSlotState(Player(index)) == PLAYER_SLOT_STATE_PLAYING) then
            set userControlledPlayers = userControlledPlayers + 1
        endif
        set index = index + 1
    endloop
    set bj_isSinglePlayer = (userControlledPlayers == 1)

    // Init sounds
    //set bj_pingMinimapSound = CreateSoundFromLabel("AutoCastButtonClick", false, false, false, 10000, 10000)
    set bj_rescueSound = CreateSoundFromLabel("Rescue", false, false, false, 10000, 10000)
    set bj_questDiscoveredSound = CreateSoundFromLabel("QuestNew", false, false, false, 10000, 10000)
    set bj_questUpdatedSound = CreateSoundFromLabel("QuestUpdate", false, false, false, 10000, 10000)
    set bj_questCompletedSound = CreateSoundFromLabel("QuestCompleted", false, false, false, 10000, 10000)
    set bj_questFailedSound = CreateSoundFromLabel("QuestFailed", false, false, false, 10000, 10000)
    set bj_questHintSound = CreateSoundFromLabel("Hint", false, false, false, 10000, 10000)
    set bj_questSecretSound = CreateSoundFromLabel("SecretFound", false, false, false, 10000, 10000)
    set bj_questItemAcquiredSound = CreateSoundFromLabel("ItemReward", false, false, false, 10000, 10000)
    set bj_questWarningSound = CreateSoundFromLabel("Warning", false, false, false, 10000, 10000)
    set bj_victoryDialogSound = CreateSoundFromLabel("QuestCompleted", false, false, false, 10000, 10000)
    set bj_defeatDialogSound = CreateSoundFromLabel("QuestFailed", false, false, false, 10000, 10000)

    // Init corpse creation triggers.
    call DelayedSuspendDecayCreate()

    // Init version-specific data
    set v = VersionGet()
    if (v == VERSION_REIGN_OF_CHAOS) then
        set bj_MELEE_MAX_TWINKED_HEROES = bj_MELEE_MAX_TWINKED_HEROES_V0
    else
        set bj_MELEE_MAX_TWINKED_HEROES = bj_MELEE_MAX_TWINKED_HEROES_V1
    endif
endfunction
I think it's more advanced than a single player handle, because a force can also have player 1, 2, 3 but not player 4 and 5.

Another option would be to pass in a boolean
Example: GetPlayerId(GetLocalPlayer()) > 4

I think the force argument is the best solution.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Oh, Arcing Floating Text! I remember using it. I think it was made by Maker... right?
Yes that is correct.

So this doesn't have a limit of 100... sweet.
This snippet has an instance limit of 8910, because it doesn't use the texttag handle.
Here the text is created via image handle. Basically it splits a string into fragments
and passes them to Ascii. From the returned value, it looks up the char in the font of your choice.

Pro:
- No instance limit of 100.
- Custom fonts can look really cool.

Con:
- Much much slower than texttags.
- A font must be imported and setup ( ca 300 kb and ~100 imported datas )
 
Top