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

TextSplat 1.3.2

Some of you might remember PitzerMike's TextSplat system. Well, this one does nearly the same his did (some features have been cut off, like gradients or monospaced textsplats or attaching a textsplat to a unit), but works with 1.24.
All features that have been cut off can be replicated with the API provided by this library.

So what exactly does this library do?
Well, it tries to replicate what texttags do, which is displaying text somewhere on the map, but has some additional features, which are outlined by the documentation.

This library uses vJass, ImageUtils, ARGB and Table.

A huge thanks goes out to PitzerMike, not only for the original incarnation of TextSplat, but also for the font i used for demonstration purposes.

18/10/09 - Version 1.0.0
- initial release

23/10/09 - Version 1.1.0
- fixed a few problems related to hitting the op-limit
- fonts now have a parent, where unknown chars will be looked up (if you dont want a font to have a parent, simply input 0 into the font.create() method).

24/10/09 - Version 1.2.0
- fixed a bug related to previous fixes concerning the op-limit
- you can now display images (smilies, ...) inside text. Refer to the documentation of both libraries for detailed information
- optimizations.

25/10/09 - Version 1.2.1
- fixed images.
- some optimizations with eye on the op-limit.
- fixed an older bug related to using the .setPosition method.

23/01/10 - Version 1.3.0
- various optimizations
- added height member
- made Font compatible to 1.23a
- you cant destroy fonts anymore (anyone who did this should burn in hell anyway, i didnt maintain backwards compat)
- requires the latest version of ImageUtils, please update it.

28/02/2010 - Version 1.3.1
- fixed errors from ImageUtils popping up

18/07/2014 - Version 1.3.2
- rewrote the layout engine (if you want to call it that) to finally handle images in a way that makes sense
- fixed errors with weird interactions between text alignment and images


Font Code + Credits + Documentation
JASS:
// *************************************************************
// *                  Font -- Version 1.3.2
// *                        by Deaod
// *************************************************************
// *
// *    CREDITS:
// *        - Vexorian (JassHelper, ARGB, Table)
// *        - PitzerMike (JassNewGenPack, original TextSplat system)
// *        - MindWorX (JassNewGenPack)
// *        - Pipedream (Grimoire)
// *        - SFilip (TESH)
// *
// *    HOW TO IMPORT:
// *        * C'n'P the code into your map
// *        * If you haven't done that already, import Table into your map
// *
// *    HOW TO USE:
// *
// *        * declare a variable of type font
// *
// *        * use font.create(font Parent) to create a new font
// *          - Parent is the font thats accessed should a char not be available in this font
// *
// *        * you can add a new symbol to the font by calling the method yourfont.addChar(string which, real width, string path).
// *          - which is the symbol you want to add to the font
// *          - width is the width of the symbol in pixels
// *          - path is the path to the image file thatll be used to display the symbol.
// *          PitzerMike attached a program for creating new fonts to his TextSplat system. I suggest you use it.
// *          The paths of the imported blps dont matter, but you should copy the width of the individual chars from the .j file that program creates.
// *          Refer to [url]http://www.wc3c.net/showthread.php?t=87798[/url] (--> Importing custom Fonts) for a guide on how to create custom fonts.
// *
// *        * you can access the width of individual characters like this: CustomFont["C"].width.
// *          To access the path associated with the character use the path attribute.
// *          Example: CustomFont["C"].path
// *          "C" can be replaced with any character that has been previously added to the font using CustomFont.addChar()
// *          The double quote character can be used like this: \" (dont be scared by the broken highlighting of TESH, itll save just fine)
// *          The backslash character like this: \\
// *
// *        * you can add custom images to a font by calling the method .addImage(string which, real width, string path)
// *          Parameters work almost exactly like the ones of .addChar(), with the exception of which being case-insensitive
// *          and "slash-insensitive" (/ and \ being the same).
// *          Images are fully compatible with parents.
// *
// *        * to access such an image you have to use CustomFont.getImage(string which)
// *          which being the same or an equivalent (slash/case-insensitivity!) of the which you passed to .addImage().
// *
// *        * Fonts are not meant to be destroyed. Just dont do it.
// *
// *************************************************************
library Font initializer Init requires Table
    
    globals
        private constant    integer             ASCII_CHAR_COUNT            = 256
        private constant    integer             COLLISION_DEPTH             = 2 // how many individual chars collide at most
    endglobals
    
    globals
        private StringTable Collisions
        private StringTable Single
        private string array CollisionMapping
        private integer array CollisionResult
    endglobals
    
    private function ASCII2Int takes string char returns integer
    local integer id
    local integer i
        set char=SubString(char, 0, 1)
        if char == "" or char == null then // manually filter out 0
            return 0
        endif
        set id=Single[char] // see if the char is a non-colliding char
        if id>0 then // if it is,
            return id // return immediately
        else // if it isnt
            set id=Collisions[char] // get the ID of the collision
            set i=0
            loop // and see which char of the two colliding ones it is
                exitwhen i>=COLLISION_DEPTH
                if CollisionMapping[id*COLLISION_DEPTH+i]==char then 
                    return CollisionResult[id*COLLISION_DEPTH+i] // found it! Return immediately, to shorten the algorithm
                endif
                set i=i+1
            endloop
        endif
        debug call BJDebugMsg("TextSplat: Faulty Setup of Collisions for ASCII2Int.")
        return 0 // this line normally shouldnt be reached. If however, it is reached, you have a faulty Setup of the Collisions
    endfunction
    
    private struct fontchar
        string path
        real width
        
        static method create takes string path, real width returns thistype
        local thistype s=.allocate()
            set s.path=path
            set s.width=width
            return s
        endmethod
    endstruct
    
    private struct fontimage
        string path
        real width
        
        static method create takes string path, real width returns thistype
        local thistype s=.allocate()
            set s.path=path
            set s.width=width
            return s
        endmethod
    endstruct
    
    struct font
        private font parent=0
        private fontchar array Symbol[ASCII_CHAR_COUNT]
        
        private StringTable Images
        
        method operator [] takes string ch returns fontchar
        local fontchar f=.Symbol[ASCII2Int(ch)]
            if f==0 then
                if .parent!=0 then
                    return .parent[ch]
                debug else
                debug     call BJDebugMsg("TextSplat: Char '"+ch+"' is not available in font "+I2S(this)+".")
                endif
            endif
            return f
        endmethod
        
        method addChar takes string which, real width, string path returns nothing
        local integer i=ASCII2Int(which)
            if i>0 then
                if .Symbol[i]==0 then
                    set .Symbol[i]=fontchar.create(path, width)
                else
                    set .Symbol[i].path=path
                    set .Symbol[i].width=width
                endif
            debug else
            debug     call BJDebugMsg("TextSplat: font.addChar: '"+which+"' could not be found in the CharMap!")
            endif
        endmethod
        
        method getImage takes string which returns fontimage
        local fontimage fi=fontimage(.Images[which])
            if fi==0 then
                if .parent!=0 then
                    return .parent.getImage(which)
                debug else
                debug     call BJDebugMsg("TextSplat: Image '"+which+"' is not available in font "+I2S(this)+".")
                endif
            endif
            return fi
        endmethod
        
        method addImage takes string which, real width, string path returns nothing
            if .Images[which]>0 then
                set fontimage(.Images[which]).path=path
                set fontimage(.Images[which]).width=width
            else
                set .Images[which]=fontimage.create(path, width)
            endif
        endmethod
        
        static method create takes font Parent returns thistype
        local thistype s=.allocate()
            set s.parent=Parent
            set s.Images=StringTable.create()
            return s
        endmethod
        
        private method destroy takes nothing returns nothing
            
        endmethod
    endstruct
    
    // What im doing here is basically precalculating some things thatll lead to a speed gain in the ASCII2Int function
    private function Init takes nothing returns nothing
        set Collisions=StringTable.create()
        set Single=StringTable.create()
        
        // First, all chars that dont collide.
        // Note that they must be !=0
        set Single["\b"]=8
        set Single["\t"]=9
        set Single["\n"]=10
        set Single["\f"]=12
        set Single["\r"]=13
        set Single[" "]=32
        set Single["!"]=33
        set Single["\""]=34
        set Single["#"]=35
        set Single["$"]=36
        set Single["%"]=37
        set Single["&"]=38
        set Single["'"]=39
        set Single["("]=40
        set Single[")"]=41
        set Single["*"]=42
        set Single["+"]=43
        set Single[","]=44
        set Single["-"]=45
        set Single["."]=46
        set Single["0"]=48
        set Single["1"]=49
        set Single["2"]=50
        set Single["3"]=51
        set Single["4"]=52
        set Single["5"]=53
        set Single["6"]=54
        set Single["7"]=55
        set Single["8"]=56
        set Single["9"]=57
        set Single[":"]=58
        set Single[";"]=59
        set Single["<"]=60
        set Single["="]=61
        set Single[">"]=62
        set Single["?"]=63
        set Single["@"]=64
        set Single["["]=91
        set Single["]"]=93
        set Single["^"]=94
        set Single["_"]=95
        set Single["`"]=96
        set Single["{"]=123
        set Single["|"]=124
        set Single["}"]=125
        set Single["~"]=126
        
        // -------------------
        
        // This is the initialization of the arrays that will map from individual chars to integers later on
        set CollisionResult[(0*COLLISION_DEPTH)+0]=65
        set CollisionResult[(1*COLLISION_DEPTH)+0]=66
        set CollisionResult[(2*COLLISION_DEPTH)+0]=67
        set CollisionResult[(3*COLLISION_DEPTH)+0]=68
        set CollisionResult[(4*COLLISION_DEPTH)+0]=69
        set CollisionResult[(5*COLLISION_DEPTH)+0]=70
        set CollisionResult[(6*COLLISION_DEPTH)+0]=71
        set CollisionResult[(7*COLLISION_DEPTH)+0]=72
        set CollisionResult[(8*COLLISION_DEPTH)+0]=73
        set CollisionResult[(9*COLLISION_DEPTH)+0]=74
        set CollisionResult[(10*COLLISION_DEPTH)+0]=75
        set CollisionResult[(11*COLLISION_DEPTH)+0]=76
        set CollisionResult[(12*COLLISION_DEPTH)+0]=77
        set CollisionResult[(13*COLLISION_DEPTH)+0]=78
        set CollisionResult[(14*COLLISION_DEPTH)+0]=79
        set CollisionResult[(15*COLLISION_DEPTH)+0]=80
        set CollisionResult[(16*COLLISION_DEPTH)+0]=81
        set CollisionResult[(17*COLLISION_DEPTH)+0]=82
        set CollisionResult[(18*COLLISION_DEPTH)+0]=83
        set CollisionResult[(19*COLLISION_DEPTH)+0]=84
        set CollisionResult[(20*COLLISION_DEPTH)+0]=85
        set CollisionResult[(21*COLLISION_DEPTH)+0]=86
        set CollisionResult[(22*COLLISION_DEPTH)+0]=87
        set CollisionResult[(23*COLLISION_DEPTH)+0]=88
        set CollisionResult[(24*COLLISION_DEPTH)+0]=89
        set CollisionResult[(25*COLLISION_DEPTH)+0]=90
        set CollisionResult[(26*COLLISION_DEPTH)+0]=47
        
        set CollisionResult[(0*COLLISION_DEPTH)+1]=97
        set CollisionResult[(1*COLLISION_DEPTH)+1]=98
        set CollisionResult[(2*COLLISION_DEPTH)+1]=99
        set CollisionResult[(3*COLLISION_DEPTH)+1]=100
        set CollisionResult[(4*COLLISION_DEPTH)+1]=101
        set CollisionResult[(5*COLLISION_DEPTH)+1]=102
        set CollisionResult[(6*COLLISION_DEPTH)+1]=103
        set CollisionResult[(7*COLLISION_DEPTH)+1]=104
        set CollisionResult[(8*COLLISION_DEPTH)+1]=105
        set CollisionResult[(9*COLLISION_DEPTH)+1]=106
        set CollisionResult[(10*COLLISION_DEPTH)+1]=107
        set CollisionResult[(11*COLLISION_DEPTH)+1]=108
        set CollisionResult[(12*COLLISION_DEPTH)+1]=109
        set CollisionResult[(13*COLLISION_DEPTH)+1]=110
        set CollisionResult[(14*COLLISION_DEPTH)+1]=111
        set CollisionResult[(15*COLLISION_DEPTH)+1]=112
        set CollisionResult[(16*COLLISION_DEPTH)+1]=113
        set CollisionResult[(17*COLLISION_DEPTH)+1]=114
        set CollisionResult[(18*COLLISION_DEPTH)+1]=115
        set CollisionResult[(19*COLLISION_DEPTH)+1]=116
        set CollisionResult[(20*COLLISION_DEPTH)+1]=117
        set CollisionResult[(21*COLLISION_DEPTH)+1]=118
        set CollisionResult[(22*COLLISION_DEPTH)+1]=119
        set CollisionResult[(23*COLLISION_DEPTH)+1]=120
        set CollisionResult[(24*COLLISION_DEPTH)+1]=121
        set CollisionResult[(25*COLLISION_DEPTH)+1]=122
        set CollisionResult[(26*COLLISION_DEPTH)+1]=92
        
        // --------------------
        
        // This maps the ID of the collision onto the colliding chars, which are stored in this virtual 2D array
        set CollisionMapping[ 0*(COLLISION_DEPTH)+0]="A"
        set CollisionMapping[ 1*(COLLISION_DEPTH)+0]="B"
        set CollisionMapping[ 2*(COLLISION_DEPTH)+0]="C"
        set CollisionMapping[ 3*(COLLISION_DEPTH)+0]="D"
        set CollisionMapping[ 4*(COLLISION_DEPTH)+0]="E"
        set CollisionMapping[ 5*(COLLISION_DEPTH)+0]="F"
        set CollisionMapping[ 6*(COLLISION_DEPTH)+0]="G"
        set CollisionMapping[ 7*(COLLISION_DEPTH)+0]="H"
        set CollisionMapping[ 8*(COLLISION_DEPTH)+0]="I"
        set CollisionMapping[ 9*(COLLISION_DEPTH)+0]="J"
        set CollisionMapping[10*(COLLISION_DEPTH)+0]="K"
        set CollisionMapping[11*(COLLISION_DEPTH)+0]="L"
        set CollisionMapping[12*(COLLISION_DEPTH)+0]="M"
        set CollisionMapping[13*(COLLISION_DEPTH)+0]="N"
        set CollisionMapping[14*(COLLISION_DEPTH)+0]="O"
        set CollisionMapping[15*(COLLISION_DEPTH)+0]="P"
        set CollisionMapping[16*(COLLISION_DEPTH)+0]="Q"
        set CollisionMapping[17*(COLLISION_DEPTH)+0]="R"
        set CollisionMapping[18*(COLLISION_DEPTH)+0]="S"
        set CollisionMapping[19*(COLLISION_DEPTH)+0]="T"
        set CollisionMapping[20*(COLLISION_DEPTH)+0]="U"
        set CollisionMapping[21*(COLLISION_DEPTH)+0]="V"
        set CollisionMapping[22*(COLLISION_DEPTH)+0]="W"
        set CollisionMapping[23*(COLLISION_DEPTH)+0]="X"
        set CollisionMapping[24*(COLLISION_DEPTH)+0]="Y"
        set CollisionMapping[25*(COLLISION_DEPTH)+0]="Z"
        set CollisionMapping[26*(COLLISION_DEPTH)+0]="/"
        
        set CollisionMapping[ 0*(COLLISION_DEPTH)+1]="a"
        set CollisionMapping[ 1*(COLLISION_DEPTH)+1]="b"
        set CollisionMapping[ 2*(COLLISION_DEPTH)+1]="c"
        set CollisionMapping[ 3*(COLLISION_DEPTH)+1]="d"
        set CollisionMapping[ 4*(COLLISION_DEPTH)+1]="e"
        set CollisionMapping[ 5*(COLLISION_DEPTH)+1]="f"
        set CollisionMapping[ 6*(COLLISION_DEPTH)+1]="g"
        set CollisionMapping[ 7*(COLLISION_DEPTH)+1]="h"
        set CollisionMapping[ 8*(COLLISION_DEPTH)+1]="i"
        set CollisionMapping[ 9*(COLLISION_DEPTH)+1]="j"
        set CollisionMapping[10*(COLLISION_DEPTH)+1]="k"
        set CollisionMapping[11*(COLLISION_DEPTH)+1]="l"
        set CollisionMapping[12*(COLLISION_DEPTH)+1]="m"
        set CollisionMapping[13*(COLLISION_DEPTH)+1]="n"
        set CollisionMapping[14*(COLLISION_DEPTH)+1]="o"
        set CollisionMapping[15*(COLLISION_DEPTH)+1]="p"
        set CollisionMapping[16*(COLLISION_DEPTH)+1]="q"
        set CollisionMapping[17*(COLLISION_DEPTH)+1]="r"
        set CollisionMapping[18*(COLLISION_DEPTH)+1]="s"
        set CollisionMapping[19*(COLLISION_DEPTH)+1]="t"
        set CollisionMapping[20*(COLLISION_DEPTH)+1]="u"
        set CollisionMapping[21*(COLLISION_DEPTH)+1]="v"
        set CollisionMapping[22*(COLLISION_DEPTH)+1]="w"
        set CollisionMapping[23*(COLLISION_DEPTH)+1]="x"
        set CollisionMapping[24*(COLLISION_DEPTH)+1]="y"
        set CollisionMapping[25*(COLLISION_DEPTH)+1]="z"
        set CollisionMapping[26*(COLLISION_DEPTH)+1]="\\"
        
        
        // --------------------
        
        // These are all the chars that collide
        // Note that i have left out their counterparts (with which they would collide)
        set Collisions["A"]= 0
        set Collisions["B"]= 1
        set Collisions["C"]= 2
        set Collisions["D"]= 3
        set Collisions["E"]= 4
        set Collisions["F"]= 5
        set Collisions["G"]= 6
        set Collisions["H"]= 7
        set Collisions["I"]= 8
        set Collisions["J"]= 9
        set Collisions["K"]=10
        set Collisions["L"]=11
        set Collisions["M"]=12
        set Collisions["N"]=13
        set Collisions["O"]=14
        set Collisions["P"]=15
        set Collisions["Q"]=16
        set Collisions["R"]=17
        set Collisions["S"]=18
        set Collisions["T"]=19
        set Collisions["U"]=20
        set Collisions["V"]=21
        set Collisions["W"]=22
        set Collisions["X"]=23
        set Collisions["Y"]=24
        set Collisions["Z"]=25
        set Collisions["/"]=26
        set Collisions["\\"]=26
    endfunction
    
endlibrary
TextSplat Code + Credits + Documentation
JASS:
// *************************************************************
// *                TextSplat -- Version 1.3.2
// *                        by Deaod
// *************************************************************
// *
// *    CREDITS:
// *        - MasterofSickness (reported bugs and gave a cool optimization idea)
// *        - Vexorian (JassHelper, ARGB, Table)
// *        - PitzerMike (JassNewGenPack, original TextSplat system)
// *        - MindWorX (JassNewGenPack)
// *        - Pipedream (Grimoire)
// *        - SFilip (TESH)
// *
// *    HOW TO IMPORT:
// *        * C'n'P the code into your map
// *        * If you haven't done that already, import ARGB and Table into your map
// *        * Set up a Font (a HOW-TO can be found inside the Font library)
// *
// *    HOW TO USE:
// *
// *        * Theres a procedural API and an object oriented API
// *          The procedural resembles the API of texttags with one exception: You have to pass a font to the CreateTextSplat function.
// *          Other than that, simply replace TextTag in the function names with TextSplat to switch over to TextSplats.
// *          Note that there might be discrepancies between texttags and textsplats concerning the more obscure features of texttags,
// *          such as the "suspended" attribute.
// *          
// *          The object oriented API, however, has more options, for example you can read the width of the textsplat in WC3 units of length.
// *
// *        * declare a variable of type textsplat
// *
// *        * use textsplat.create(font Font) to create a new image
// *            - Refer to the documentation of the Font library for detailed information on Fonts
// *
// *        * you can change the text of the textsplat by calling the method yourtsplat.setText(string text, real height, integer aligntype).
// *            - text is the new text the textsplat should read
// *            - height is the fontsize of the text
// *            - aligntype is where the text should be aligned. Possible aligntypes:
// *              TEXTSPLAT_TEXT_ALIGN_LEFT, TEXTSPLAT_TEXT_ALIGN_CENTER, TEXTSPLAT_TEXT_ALIGN_RIGHT
// *          you can embed images into text (as long as the Font supports the embedded images) by surrounding the images identifier by "|i"
// *          Example: "|igold|i" would be converted into the gold coin image (if the font supports it).
// *          Note that image idenitifiers are case- and slash-insensitive (the latter meaning that / == \). "|ilumber|i" displays the same image as
// *          "|iLuMBeR|i" does.
// *
// *        * you can change the velocity of a textsplat by calling the method yourtsplat.setVelocity(real xvel, real yvel, real zvel)
// *          Velocities are in (WC3 units of length)/second
// *
// *        * you can change the color of the textsplat by calling the method yourtsplat.setColor(integer red, integer green, integer blue, integer alpha)
// *          All parameters can take values from 0 to 255 (including 0 and 255).
// *          This changes the color of all chars outside |cAARRGGBB - |r tags.
// *          Changes in the value of the alpha channel are prohibited if the textsplat is currently fading.
// *          Note that textsplats, unlike texttags, have a working alpha channel.
// *
// *        * you can change the position of a textsplat by calling the method yourtsplat.setPosition(real x, real y, real z)
// *          the point specified is the lower left corner of the textsplat.
// *          Theres also a variant of this function (yourtsplat.setPosUnit(unit u, real z)) that aligns the textsplats lower left corner
// *          to the units origin and with the specified z offset (from 0., not the units locations z).
// *
// *        * you can access the standard attributes (lifespan, fadepoint, age, permanent, visible) of texttags as variable members of the struct.
// *          Example: set yourtsplat.fadepoint=2.
// *          Note that some things may not work exactly like they did with texttags. If you notice such a discrepancy, tell me about it.
// *
// *        * you can access the width of the textsplat by reading the width member of the struct (yourtsplat.width).
// *          Note that this member is read only.
// *          It returns the width of the textsplat in standard WC3 units of length.
// *
// *        * you can access the height of the textsplat by read the height member of the structs (yourtsplat.height).
// *          This returns the Y size of the textsplat, much like the width member returns the X size.
// *          The returned values are in standard WC3 units of length.
// *
// *        * you can access and change the font of a textsplat on the fly by reading/writing the .font member
// *          Note that any changes to the font only take place after the next call to .setText()
// *
// *        * to destroy a textsplat use yourtsplat.destroy()
// *
// *************************************************************

library TextSplat initializer Init uses ARGB, ImageUtils, Table, Font
    
    globals
        private constant    real                    TICK                        = 1./40
        
        private constant    integer                 DEFAULT_COLOR               = 0xFFFFFFFF // white
        private constant    integer                 DEFAULT_IMAGE_TYPE          = IMAGE_TYPE_SELECTION
                constant    integer                 DEFAULT_IMAGE_SIZE          = 32 // If you dont know what this is used for, you might want to leave it alone to not fuck things up
        
                constant    integer                 TEXTSPLAT_TEXT_ALIGN_LEFT   = 1
                constant    integer                 TEXTSPLAT_TEXT_ALIGN_CENTER = 2
                constant    integer                 TEXTSPLAT_TEXT_ALIGN_RIGHT  = 3
        
                constant    real                    TEXT_SIZE_TO_IMAGE_SIZE     = 4.146479
        
        private constant    integer                 TOKENS_PER_CHUNK             = 40 // lower this if you make heavy use of |cAARRGGBB strings in textsplats. Maximum working value is about 65.
    endglobals
    
    
    
    globals
        private real array TextAlignBonus
        private StringTable Hex2Dec
        private StringTable IsHex
    endglobals

    private struct char
        image Img
        string Path
        real SizeX
        real SizeY
        real X
        real Y
        real Z
        integer Type
        boolean Show
        boolean DefaultColor
        integer ColorAlpha
        integer ColorRed
        integer ColorBlue
        integer ColorGreen
        
        private method Recreate takes nothing returns nothing
            if .Img!=null then
                call ReleaseImage(.Img)
            endif
            set .Img=NewImage(.Path, .SizeX, .SizeY, 0, .X, .Y, 0, 0, 0, 0, .Type)
            call SetImageConstantHeight(.Img, true, .Z)
            call SetImageColor(.Img, .ColorRed, .ColorGreen, .ColorBlue, .ColorAlpha)
            call SetImageRenderAlways(.Img, .Show)
        endmethod
        
        method operator color= takes ARGB col returns nothing
            set .ColorAlpha=col.alpha
            set .ColorRed=col.red
            set .ColorGreen=col.green
            set .ColorBlue=col.blue
            call SetImageColor(.Img, .ColorRed, .ColorGreen, .ColorBlue, .ColorAlpha)
        endmethod
        
        private static method create takes string path, real sizex, real sizey, real x, real y, real z returns thistype
        local thistype s=.allocate()
            set s.Path=path
            set s.SizeX=sizex
            set s.SizeY=sizey
            set s.X=x
            set s.Y=y
            set s.Z=z
            set s.Show=true
            set s.Type=DEFAULT_IMAGE_TYPE
            set s.color=DEFAULT_COLOR
            call s.Recreate()
            return s
        endmethod
        
        static method createChar takes font f, string ch, real x, real y, real z, real size returns thistype
            return .create(f[ch].path, size*TEXT_SIZE_TO_IMAGE_SIZE, size*TEXT_SIZE_TO_IMAGE_SIZE, x, y, z)
        endmethod
        
        static method createImage takes font f, string img, real x, real y, real z, real size returns thistype
            return .create(f.getImage(img).path, size*TEXT_SIZE_TO_IMAGE_SIZE, size*TEXT_SIZE_TO_IMAGE_SIZE, x, y, z)
        endmethod
        
        method onDestroy takes nothing returns nothing
            if .Img!=null then
                call ReleaseImage(.Img)
            endif
            set .Img=null
        endmethod
    endstruct
    
    private struct TextToken extends array
        integer tokenType
        string value
        ARGB valueColor
        boolean isDefaultColor
        
        integer line
        real posX
        real posY
        
        static constant integer TOKEN_TYPE_INVALID = 0
        static constant integer TOKEN_TYPE_NORMAL = 1
        static constant integer TOKEN_TYPE_NEWLINE = 2
        static constant integer TOKEN_TYPE_COLOR = 3
        static constant integer TOKEN_TYPE_COLOREND = 4
        static constant integer TOKEN_TYPE_IMAGE = 5
    endstruct
    
    globals
        private real array LineWidth
        private string array TextChars
        private real MaxLineWidth
        private integer TSPCount
        private integer TSPSourceStringLength
        private integer CurrentLine
        private ARGB CurrentColor
        private boolean IsDefaultColor
        private char TSPChar
        private real SourceX
        private real SourceY
        
        private integer TokenAmount
        private TextToken CurrentToken
        
        private location LocZ=Location(0,0)
    endglobals
    
    private function IsHexadecimal takes integer offset returns boolean
    local integer i = offset
    local integer l = i + 8
        loop
            exitwhen i >= l
            if IsHex[TextChars[i]] == 0 then
                // one illegal char aborts
                return false
            endif
            set i=i+1
        endloop
        return true
    endfunction
    
    private function prepareText takes string text returns nothing
    local integer i = 0
        loop
            exitwhen i >= TSPSourceStringLength
            set TextChars[i] = SubString(text, i, i+1)
            set i = i + 1
        endloop
        
        // zero termination
        set TextChars[i] = null
    endfunction
    
    // this is a bit hacky... but necessary to avoid hitting the op limit.
    private function tokenizeText takes TextToken base returns nothing
    local integer i = TSPCount
    local string curChar
    local TextToken token = CurrentToken
        loop
            exitwhen (integer(token) - integer(base) >= TOKENS_PER_CHUNK) or (i >= TSPSourceStringLength)
            set curChar = TextChars[i]
            
            // check for \n or \r\n or |n
            if curChar=="\n" or (curChar=="\r" and TextChars[i + 1] == "\n") or (curChar=="|" and TextChars[i + 1]=="n") then
                set token.tokenType = TextToken.TOKEN_TYPE_NEWLINE
                set token = TextToken(integer(token) + 1)
                
                if curChar != "\n" then
                    set i = i + 1
                endif
            elseif curChar=="|" and TextChars[i + 1] == "c" and TSPSourceStringLength - i >= 10 and IsHexadecimal(i + 2) then
                set token.tokenType = TextToken.TOKEN_TYPE_COLOR
                set token.valueColor = ARGB.create(Hex2Dec[TextChars[i + 2] + TextChars[i + 3]], Hex2Dec[TextChars[i + 4] + TextChars[i + 5]], Hex2Dec[TextChars[i + 6] + TextChars[i + 7]], Hex2Dec[TextChars[i + 8] + TextChars[i + 9]])
                set token = TextToken(integer(token) + 1)
                
                set i = i + 9
            elseif curChar=="|" and TextChars[i + 1] == "r" then
                set token.tokenType = TextToken.TOKEN_TYPE_COLOREND
                set token = TextToken(integer(token) + 1)
                
                set i = i + 1
            elseif curChar == "|" and TextChars[i + 1] == "i" then
                set i = i + 2
                
                set token.tokenType = TextToken.TOKEN_TYPE_IMAGE
                set token.value = ""
                loop
                    exitwhen (TextChars[i] == "|" and TextChars[i + 1] == "i") or (i >= TSPSourceStringLength)
                    set token.value = token.value + TextChars[i]
                    set i = i + 1
                endloop
                
                set token = TextToken(integer(token) + 1)
                
                set i = i + 1
            else // normal char
                set token.tokenType = TextToken.TOKEN_TYPE_NORMAL
                set token.value = curChar
                set token = TextToken(integer(token) + 1)
            endif
            
            set i = i + 1
        endloop
        
        set CurrentToken = token
        set TSPCount = i
    endfunction
    
    private function layoutText takes TextToken base, ARGB backgroundColor, font textFont, real lineHeight returns nothing
    local TextToken token = CurrentToken
    local real width
        loop
            exitwhen (integer(token) - integer(base) >= TOKENS_PER_CHUNK) or (integer(token) >= TokenAmount)
            
            if token.tokenType == TextToken.TOKEN_TYPE_NEWLINE then
                if LineWidth[CurrentLine] > MaxLineWidth or CurrentLine == 0 then
                    set MaxLineWidth = LineWidth[CurrentLine]
                endif
                set CurrentLine = CurrentLine + 1
                set LineWidth[CurrentLine] = 0
            elseif token.tokenType == TextToken.TOKEN_TYPE_COLOR then
                set CurrentColor = token.valueColor
                set IsDefaultColor = false
            elseif token.tokenType == TextToken.TOKEN_TYPE_COLOREND then
                set CurrentColor = backgroundColor
                set IsDefaultColor = true
            elseif token.tokenType == TextToken.TOKEN_TYPE_IMAGE or token.tokenType == TextToken.TOKEN_TYPE_NORMAL then
                set token.valueColor = CurrentColor
                set token.isDefaultColor = IsDefaultColor
                set token.line = CurrentLine
                if token.tokenType == TextToken.TOKEN_TYPE_IMAGE then
                    set width = (textFont.getImage(token.value).width * TEXT_SIZE_TO_IMAGE_SIZE * lineHeight / DEFAULT_IMAGE_SIZE)
                else
                    set width = (textFont[token.value].width * TEXT_SIZE_TO_IMAGE_SIZE * lineHeight / DEFAULT_IMAGE_SIZE)
                endif
                set token.posY = CurrentLine * lineHeight * TEXT_SIZE_TO_IMAGE_SIZE
                set token.posX = LineWidth[CurrentLine]
                
                set LineWidth[CurrentLine] = LineWidth[CurrentLine] + width
            endif
            
            set token = TextToken(integer(token) + 1)
        endloop
        
        set CurrentToken = token
    endfunction
    
    private function displayText takes TextToken base, real lineHeight, integer alignType, real z, boolean visible, font textFont, Table t returns nothing
    local integer i
    local boolean b
    local TextToken token = CurrentToken
    local real x
    local real y
        
        loop
            exitwhen (integer(token) - integer(base) >= TOKENS_PER_CHUNK) or (integer(token) >= TokenAmount)
            
            if token.tokenType == TextToken.TOKEN_TYPE_IMAGE or token.tokenType == TextToken.TOKEN_TYPE_NORMAL then
                set x = SourceX + token.posX + ( TextAlignBonus[alignType] * (MaxLineWidth - LineWidth[token.line]) )
                set y = SourceY - token.posY
                
                if token.tokenType == TextToken.TOKEN_TYPE_IMAGE then
                    set TSPChar = char.createImage(textFont, token.value, x, y, z, lineHeight)
                else
                    set TSPChar = char.createChar(textFont, token.value, x, y, z, lineHeight)
                endif
                
                set TSPChar.DefaultColor = token.isDefaultColor
                set TSPChar.color = token.valueColor
                set TSPChar.Show = visible
                call SetImageRenderAlways(TSPChar.Img, visible)
                set t[TSPCount] = integer(TSPChar)
                set TSPCount=TSPCount+1
            endif
            
            set token = TextToken(integer(token) + 1)
        endloop
        
        set CurrentToken = token
    endfunction
    
    // destroys all chars of the textsplat
    private function cleanSplat takes Table t, integer count returns nothing
    local integer i=count-1
    local integer j
        loop
            exitwhen i<0
            set j=t[i]
            if j>0 then
                call char(j).destroy()
            endif
            set i=i-1
        endloop
    endfunction
    
    struct textsplat
        private font F
        
        private Table Chars
        private integer CharCount=0
        
        private real X=0.
        private real Y=0.
        private real Z=0.
        
        private real Dx=0.
        private real Dy=0.
        private real Dz=0.
        
        private real Age=0.
        private real Lifespan=0.
        private real Fadepoint=0.
        
        private real Width=0.
        private real Height=0.
        
        private boolean Suspended=false
        private boolean Permanent=true
        private boolean Visible=true
        
        private string Text=""
        private ARGB BGColor=ARGB(DEFAULT_COLOR)
        
        private integer i
        
        private static thistype array Structs
        private static timer T=CreateTimer()
        private static integer Count=0
        
        method onDestroy takes nothing returns nothing
        local integer i=0
            call cleanSplat.evaluate(.Chars, .CharCount)
            call .Chars.destroy()
            
            // clean your struct here
            set .Count=.Count-1
            set .Structs[.i]=.Structs[.Count]
            set .Structs[.i].i=.i
            if .Count==0 then
                call PauseTimer(.T)
            endif
        endmethod
        
        private static method Callback takes nothing returns nothing
        local integer i=.Count-1
        local thistype s
        local integer j
        local char ch
        local boolean b1
        local real alphafactor
            loop
                exitwhen i<0
                set s=.Structs[i]
                if (not s.Suspended) then
                    if s.Lifespan>s.Age then
                        set s.Age=s.Age+TICK
                        if s.Age>=s.Lifespan then
                            if s.Permanent then
                                set s.Suspended=true
                            else
                                call s.destroy()
                            endif
                        endif
                    endif
                    if (s.Dx!=0 or s.Dy!=0 or s.Dz!=0) then
                        set s.X=s.X+s.Dx
                        set s.Y=s.Y+s.Dy
                        set s.Z=s.Z+s.Dz
                        set j=s.CharCount-1
                        set b1=s.Dz!=0.
                        if s.Age>s.Fadepoint then
                            set alphafactor=1-(s.Age-s.Fadepoint)/(s.Lifespan-s.Fadepoint)
                        else
                            set alphafactor=1.
                        endif
                        loop
                            exitwhen j<0
                            set ch=s.Chars[j]
                            if ch>0 then
                                set ch.X=ch.X+s.Dx
                                set ch.Y=ch.Y+s.Dy
                                call SetImagePosition(ch.Img, ch.X, ch.Y, 0)
                                if b1 then // s.Dz!=0.
                                    set ch.Z=ch.Z+s.Dz
                                    call SetImageConstantHeight(ch.Img, true, ch.Z)
                                endif
                                call SetImageColor(ch.Img, ch.ColorRed, ch.ColorGreen, ch.ColorBlue, R2I(ch.ColorAlpha*alphafactor))
                            endif
                            set j=j-1
                        endloop
                    endif
                endif
                set i=i-1
            endloop
        endmethod
        
        method setText takes string text, real height, integer aligntype returns nothing
            set TSPSourceStringLength=StringLength(text)
            set CurrentLine=0
            set CurrentColor=.BGColor
            set IsDefaultColor = true
            set MaxLineWidth=0
            
            // lets clean up old stuff we dont need anymore
            call cleanSplat.evaluate(.Chars, .CharCount)
            
            call prepareText.evaluate(text)
            
            set LineWidth[0]=0
            //
            set TSPCount=0
            set CurrentToken = TextToken(0)
            loop
                exitwhen TSPCount >= TSPSourceStringLength // TSPCount gets incremented in processText
                call tokenizeText.evaluate(CurrentToken)
            endloop
            
            set TokenAmount = integer(CurrentToken)
            
            set CurrentToken = TextToken(0)
            loop
                exitwhen integer(CurrentToken) >= TokenAmount
                call layoutText.evaluate(CurrentToken, .BGColor, .F, height)
            endloop
            
            if CurrentLine == 0 or LineWidth[CurrentLine] > MaxLineWidth then
                set MaxLineWidth = LineWidth[CurrentLine]
            endif
            
            // actually display the shit
            set SourceX = .X
            set SourceY = .Y + CurrentLine * height * TEXT_SIZE_TO_IMAGE_SIZE
            set TSPCount=0
            set CurrentToken = TextToken(0)
            loop
                exitwhen integer(CurrentToken) >= TokenAmount
                call displayText.evaluate(CurrentToken, height, aligntype, .Z, .Visible, .F, .Chars)
            endloop
            
            set .CharCount = TSPCount
            set .Text = text
            set .Width = MaxLineWidth
            set .Height = (CurrentLine + 1) * height * TEXT_SIZE_TO_IMAGE_SIZE
        endmethod
        
        method setVelocity takes real xvel, real yvel, real zvel returns nothing
            set .Dx = xvel * TICK
            set .Dy = yvel * TICK
            set .Dz = zvel * TICK
        endmethod
        
        method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
        local integer i=0
        local char ch
        local real alphafactor
            set .BGColor=ARGB.create(alpha, red, green, blue)
            if .Age>.Fadepoint and not(.Lifespan==.Fadepoint) then
                set alphafactor=1-(.Age-.Fadepoint)/(.Lifespan-.Fadepoint)
            else
                set alphafactor=1.
            endif
            loop
                exitwhen i>=.CharCount
                set ch=char(.Chars[i])
                if ch>0 and ch.DefaultColor then
                    set ch.ColorRed=red
                    set ch.ColorGreen=green
                    set ch.ColorBlue=blue
                    set ch.ColorAlpha=alpha
                    call SetImageColor(ch.Img, red, green, blue, R2I(alpha*alphafactor))
                endif
                set i=i+1
            endloop
        endmethod
        
        method setPosition takes real x, real y, real z returns nothing
            local integer i=.CharCount-1
            local real dx=x-.X
            local real dy=y-.Y
            local real dz=z-.Z
            local char c
            set .X=x
            set .Y=y
            set .Z=z
            loop
                exitwhen i<0
                set c=char(.Chars[i])
                if c>0 then
                    set c.X=c.X+dx
                    set c.Y=c.Y+dy
                    set c.Z=c.Z+dz
                    call SetImagePosition(c.Img, c.X, c.Y, 0)
                    call SetImageConstantHeight(c.Img, true, c.Z)
                endif
                set i=i-1
            endloop
        endmethod
        
        method setPosUnit takes unit u, real z returns nothing
            call .setPosition(GetUnitX(u), GetUnitY(u), z)
        endmethod
        
        method operator age takes nothing returns real
            return .Age
        endmethod
        method operator lifespan takes nothing returns real
            return .Lifespan
        endmethod
        method operator fadepoint takes nothing returns real
            return .Fadepoint
        endmethod
        method operator width takes nothing returns real
            return .Width
        endmethod
        method operator height takes nothing returns real
            return .Height
        endmethod
        method operator suspended takes nothing returns boolean
            return .Suspended
        endmethod
        method operator permanent takes nothing returns boolean
            return .Permanent
        endmethod
        method operator visible takes nothing returns boolean
            return .Visible
        endmethod
        method operator font takes nothing returns font
            return .F
        endmethod
        
        method operator age= takes real new returns nothing
            set .Age=new
        endmethod
        method operator lifespan= takes real new returns nothing
            set .Lifespan=new
        endmethod
        method operator fadepoint= takes real new returns nothing
            set .Fadepoint=new
        endmethod
        //
        // -- Width is readonly
        //
        //
        // -- Height is readonly
        //
        method operator suspended= takes boolean flag returns nothing
            set .Suspended=flag
        endmethod
        method operator permanent= takes boolean flag returns nothing
            set .Permanent=flag
        endmethod
        method operator visible= takes boolean flag returns nothing
        local integer i=0
        local char c
            set .Visible=flag
            loop
                exitwhen i>=.CharCount
                set c=char(.Chars[i])
                if c>0 then
                    set c.Show=flag
                    call SetImageRenderAlways(c.Img, flag)
                endif
                set i=i+1
            endloop
        endmethod
        method operator font= takes font F returns nothing
            set .F=F
        endmethod
        
        static method create takes font F returns thistype
        local thistype s=.allocate()
            set s.F=F
            set s.Chars=Table.create()
            
            set .Structs[.Count]=s
            set s.i=.Count
            if .Count==0 then
                call TimerStart(.T, TICK, true, function thistype.Callback)
            endif
            set .Count=.Count+1
            return s
        endmethod
    endstruct
    
    // Standard API
    //   a replacement for the TextTag API
    function CreateTextSplat takes font F returns textsplat
        return textsplat.create(F)
    endfunction
    function DestroyTextSplat takes textsplat which returns nothing
        call which.destroy()
    endfunction
    function SetTextSplatAge takes textsplat t, real age returns nothing
        set t.age=age
    endfunction
    function SetTextSplatColor takes textsplat t, integer red, integer green, integer blue, integer alpha returns nothing
        call t.setColor(red, green, blue, alpha)
    endfunction
    function SetTextSplatFadepoint takes textsplat t, real fadepoint returns nothing
        set t.fadepoint=fadepoint
    endfunction
    function SetTextSplatLifespan takes textsplat t, real lifespan returns nothing
        set t.lifespan=lifespan
    endfunction
    function SetTextSplatPermanent takes textsplat t, boolean flag returns nothing
        set t.permanent=flag
    endfunction
    function SetTextSplatPos takes textsplat t, real x, real y, real heightOffset returns nothing
        call t.setPosition(x,y, heightOffset)
    endfunction
    function SetTextSplatPosUnit takes textsplat t, unit whichUnit, real heightOffset returns nothing
        call t.setPosUnit(whichUnit, heightOffset)
    endfunction
    function SetTextSplatSuspended takes textsplat t, boolean flag returns nothing
        set t.suspended=flag
    endfunction
    function SetTextSplatText takes textsplat t, string s, real height returns nothing
        call t.setText(s, height, TEXTSPLAT_TEXT_ALIGN_CENTER)
    endfunction
    function SetTextSplatVelocity takes textsplat t, real xvel, real yvel returns nothing
        call t.setVelocity(xvel, yvel, 0)
    endfunction
    function SetTextSplatVisibility takes textsplat t, boolean flag returns nothing
        set t.visible=flag
    endfunction
    
    //! textmacro Hex2DecUpper_Macro takes L
        set Hex2Dec["$L$0"]=0x$L$0
        set Hex2Dec["$L$1"]=0x$L$1
        set Hex2Dec["$L$2"]=0x$L$2
        set Hex2Dec["$L$3"]=0x$L$3
        set Hex2Dec["$L$4"]=0x$L$4
        set Hex2Dec["$L$5"]=0x$L$5
        set Hex2Dec["$L$6"]=0x$L$6
        set Hex2Dec["$L$7"]=0x$L$7
        set Hex2Dec["$L$8"]=0x$L$8
        set Hex2Dec["$L$9"]=0x$L$9
        set Hex2Dec["$L$A"]=0x$L$A
        set Hex2Dec["$L$B"]=0x$L$B
        set Hex2Dec["$L$C"]=0x$L$C
        set Hex2Dec["$L$D"]=0x$L$D
        set Hex2Dec["$L$E"]=0x$L$E
        set Hex2Dec["$L$F"]=0x$L$F
    //! endtextmacro
    
    private function Init takes nothing returns nothing
        set Hex2Dec=StringTable.create()
        //! runtextmacro Hex2DecUpper_Macro("0")
        //! runtextmacro Hex2DecUpper_Macro("1")
        //! runtextmacro Hex2DecUpper_Macro("2")
        //! runtextmacro Hex2DecUpper_Macro("3")
        //! runtextmacro Hex2DecUpper_Macro("4")
        //! runtextmacro Hex2DecUpper_Macro("5")
        //! runtextmacro Hex2DecUpper_Macro("6")
        //! runtextmacro Hex2DecUpper_Macro("7")
        //! runtextmacro Hex2DecUpper_Macro("8")
        //! runtextmacro Hex2DecUpper_Macro("9")
        //! runtextmacro Hex2DecUpper_Macro("A")
        //! runtextmacro Hex2DecUpper_Macro("B")
        //! runtextmacro Hex2DecUpper_Macro("C")
        //! runtextmacro Hex2DecUpper_Macro("D")
        //! runtextmacro Hex2DecUpper_Macro("E")
        //! runtextmacro Hex2DecUpper_Macro("F")
        
        set IsHex=StringTable.create()
        set IsHex["0"]=1
        set IsHex["1"]=1
        set IsHex["2"]=1
        set IsHex["3"]=1
        set IsHex["4"]=1
        set IsHex["5"]=1
        set IsHex["6"]=1
        set IsHex["7"]=1
        set IsHex["8"]=1
        set IsHex["9"]=1
        set IsHex["A"]=1
        set IsHex["B"]=1
        set IsHex["C"]=1
        set IsHex["D"]=1
        set IsHex["E"]=1
        set IsHex["F"]=1
        
        set TextAlignBonus[TEXTSPLAT_TEXT_ALIGN_LEFT    ] = 0.0
        set TextAlignBonus[TEXTSPLAT_TEXT_ALIGN_CENTER  ] = 0.5
        set TextAlignBonus[TEXTSPLAT_TEXT_ALIGN_RIGHT   ] = 1.0
    endfunction
    
endlibrary

NOTE: Fonts are prone to hitting the op-limit when initializing. For that reason i suggest you only initialize one font per library initializer, and only use the ASCII set of characters.

I attached a (really simple) demo map demonstrating basic usage of this library.

You might wonder why i split it up into two libraries (Font and TextSplat). Well, someone might want to remake TextSplat, but keep Font as a foundation, and i think its more readable that way. But i dont think the Font library has any use outside the context of a TextSplat library, so i combined those two.

Morpheus + Tooltip Images (by Earth-Fury)

If you have a font you want to share, contact me with a link to where you published your font and ill add it to this list!


Keywords:
TextSplat, TextTag, image, text
Contents

TextSplat (Map)

Reviews
17:31, 19th Oct 2009 The_Reborn_Devil: The code looks pretty good and this is definitely useful. It's well documented too. Status: Approved. Rating: Recommended.

Moderator

M

Moderator

17:31, 19th Oct 2009
The_Reborn_Devil:

The code looks pretty good and this is definitely useful.
It's well documented too.
Status: Approved.
Rating: Recommended.
 
Level 14
Joined
Nov 18, 2007
Messages
816
Okay, shut up about lag. If you know a more efficient way to do this, tell me and ill update.
Its not laggy (and most probably wont ever be the primary cause of lag).

Also, please TRY to look for things you dont understand, or that you think are bad. I cant improve this resource (and myself) if everyone says "looks good".

Also, i never said its a good alternative to texttags. In some cases texttags are just as good or even better. But people who are restrained by the texttag limit might want to look into this. Itd also be cool for those wanting to center text on units.
 
Okay, shut up about lag. If you know a more efficient way to do this, tell me and ill update.
There is none, and it has its reasons.

Its not laggy (and most probably wont ever be the primary cause of lag).
Textsplats are totally diffrent handled by the wc3 engine, they use more memory and I still see no reason why anyone needs over 90 texttags at the same time. Thats just crazy.
 
Level 14
Joined
Nov 18, 2007
Messages
816
If you actually read the damn documentation, youd know how.

JASS:
// * * you can add custom images to a font by calling the method .addImage(string which, real width, string path)
// * Parameters work almost exactly like the ones of .addChar(), with the exception of which being case-insensitive
// * and "slash-insensitive" (/ and \ being the same).
// * Images are fully compatible with parents.

// * you can embed images into text (as long as the Font supports the embedded images) by surrounding the images identifier by "|i"
// * Example: "|igold|i" would be converted into the gold coin image (if the font supports it).
// * Note that image idenitifiers are case- and slash-insensitive (the latter meaning that / == \). "|ilumber|i" displays the same image as
// * "|iLuMBeR|i" does.

Edit: I could swear Anachron had something to say too.
 
Level 21
Joined
Dec 9, 2007
Messages
3,096
Please answer quick!
How do I center the text position on the top left corner?
I'm getting mad here!
(I've been trying to find out how for some time!)

(Obviously I have to switch some "-" operators with "+" operators or vice-versa, but I don't know the function/method...)
 
Level 14
Joined
Nov 18, 2007
Messages
816
Changing the z offset is absolutely possible. Its even documented.

JASS:
// *        * you can change the position of a textsplat by calling the method yourtsplat.setPosition(real x, real y, real z)
// *          the point specified is the lower left corner of the textsplat.
// *          Theres also a variant of this function (yourtsplat.setPosUnit(unit u, real z)) that aligns the textsplats lower left corner
// *          to the units origin and with the specified z offset (from 0., not the units locations z).
 
Top