• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Snippet] TextTag

That gives me an idea for a timer struct wrapper thing.. similar to TimerUtils except that you pass around integers instead, making data attachment very easy. GetExpiredTimer will ofc return an integer ;p.

It'll also recycle timers... when it recycles, it does not clean up from the table. It only sets the table in the case of a brand new instanced timer. This should improve creation/destruction time : ).

I'll also run it off of a hashtable to improve read/write speeds rather than Table.


From here, it'll be attached[Timer.expired].destroy() : D
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Is it safe to use all these methods in a local block ? (if GetLocalPlayer() == ...).
I'm too lazy to check myself.

Because what is good with texttags, they can be handled in a local block (even create/destroy), this way you reach the limit of 100 texttags later, or even never (it's not 99, the handle id 0 is both for the last created texttag and an invalid one, blame Blizzard for this lame convention)

Honestly i fail to see the usefulness, i mean native functions seems enough, but i've not played that much with texttags.
 
You can't create local texttags as that would desync the handle table.

Native functions require you to know how to convert the sizes and velocities. Also, they don't have anything for locking to a unit, they just set the texttag to the unit's position, which is off anyways as it won't be centered.

This allows you to lock to any position, not just units, and will allow you to center it by using relative x,y coords. This also makes it so that perm texttags are never destroyed and this just has an overall easier API to work with than the natives : ).
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Do what you want, but if it's not possible to make it "safe" (even with some explicit requirements) in a local block, then it should be stated in the documentation.

Also, i repeat myself the limit is 100, not 99, it's not hard to test it by yourself.
I would even share you a jass code, but last time i've said it, i used a GUI code in GUI tags.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Just to be sure (no offense i just don't know your knowledge/skills), local block : if GetLocalPlayer() ==... != local texttag txt =...

Also, to be more clear, some people are afraid about a possible desync due to the internal string table, i don't believe that much in that, just because wc3 exist in many languages, and there are localized strings everywhere.
But more safety can't be bad, what you have to do is to define the string outside the local block and you're fine.
 
The only thing you have to worry about is local data because if you use it in a condition, the following code will most likely be performed locally. But that is obvious.

Anyway, Nes I recommend you just allow them to input a force. Then you can just do:
JASS:
if IsPlayerInForce(GetLocalPlayer(), f) then

That way, people can either do it for forces or just use bj_forcePlayer[x] or whatever it is.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Laiev is wrong, the id are from 99 to 0, where 0 could be both the id of the last created texttag and an invalid one.
That's why many people do the mistake, but you can sucessfully create (and display) 100 texttags simultaneously.
If you create more than 100 texttags, one of the previous ones will be destroyed, i've not checked if there was an obvious link.

The images id start to 0 and increase (i don't know the limit, if there is even a practical one), they also can be handled in a local block.

@PurgeAndFire111 :

I don't get what you want mean, opening a thread in a local block wouldn't cause a desync ? (ForForce opens a new thread)
And anyway i'm lost with your suggestion of a force usage, why you would need this ?
 
Level 7
Joined
Oct 11, 2008
Messages
304
>.> What I mean in the MSN is, the last id when I tested, was 99, but I didn't test the first id LOL

Also, if I remember right, if you create more then the limit of texttags, the first/last ids will bug and you can't interact with it anymore.
 
Sheesh, why don't you guys test it right now instead of depending on past tests? ._.

Here, I even wrote some code for you :3

JASS:
struct TexttagTest extends array

    static method print takes string s returns nothing
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, s)
    endmethod

    static method onInit takes nothing returns nothing
        local texttag t = null
        local integer count = 0
        local integer minId = 9001
        local integer maxId = 0

        loop
            set t = CreateTextTag()
            exitwhen t == null
            set count = count + 1
            if GetHandleId(t) < minId then
                set minId = GetHandleId(t)
            elseif GetHandleId(t) > maxId then
                set maxId = GetHandleId(t)
            endif
        endloop

        call print("Max texttags: " + I2S(count))
        call print("Min Id: " + I2S(minId))
        call print("Max Id: " + I2S(maxId))

        set t = null
    endmethod
endstruct

edit
Nestharus posted while I was typing ._.
Bah, I realized that my code wouldn't test it correctly... nevermind.
 
actually, my test was wrong. Id 0 continues to be used for new tags ;)

edit
Fixed a couple of bugs and made local texttags possible. Haven't tested them for desyncs as I can't :\. Have to wait on someone else to verify that they work ; ).

You have to use the createLocal method to prevent the desync =P

With this, this should now be superior to texttags in functionality ;D.

edit
would anyone be willing to help me verify that this does not desync in multiplayer?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I'm too lazy to really think about that but since you are playing with timer(s) i hardly think all methods can be used in a local block.

Also, instead of a boolean, a better way is probably to take a player argument for local stuff.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
All methods can be used with timers... so there... >.>

I'm just saying that run/pause timer in a local block would cause a desync. (but i've never tested it, i'm just making the assumption that opening a new thread in a local block will lead to a desync by itself)

It works just fine.

So use the destroy in a local block is fine ?

JASS:
method destroy takes nothing returns nothing
            call DestroyTextTag(tag_p)
            @call PauseTimer(textTagTimer_p)@ // in some computer the timer will be paused, in other it will still running, and then opening new threads


And boolean is better than a player argument. I chose it purposefully.

I know you did it to avoid more lines of code, but that doesn't really matter, to display a texttag only for the player it just makes more sense to use a player argument imho, rather than a local block.
But i can miss something.

The only things that aren't synced are the texttags. Everything else is sync'd exactly. I'm not a noob, lol.

The problem is not your skills/knowledge, it is your ego.
 
Why would you do destroy in a local block?

You don't seem to understand how the this works for local...

You don't use local blocks at all... if you want local, use createLocal, no local blocks.

If you destroy, don't use a local block... it'll destroy it properly.


The TextTag object is created for all players, but the texttag inside is only created for local players. tag_p would be null if the player doesn't have it, so destroy would be destroying a null text tag.

The thing works if you use it right... if you try to use local blocks, then ofc it's going to desync... you asked for local texttags, not local timers...
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Thx, but i know what are local blocks.

Oh wait, you're still creating texttags for all players ?!

EDIT : Seems not, according to your edit, i will have a rest and take a better look to the code after.

Plz avoid mass "...", that doesn't make your sentences or yourself clever.
 
Let me save you the trouble of looking through.

This is where all of the magic takes place (in the allocate method)
JASS:
            if (createLocal) then
                set tagPointer_p = recyclerPointer[0]
                if (0 == tagPointer_p) then
                    set tagPointer_p = localRemaining
                    if (0 == tagPointer_p) then
                        set tagPointer_p = thistype(0).next
                        
                        debug if (0 == tagPointer_p) then
                            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "Text Tag Overload: Too Many Permanent Text Tags")
                            debug set tagPointer_p = 1/0
                        debug endif
                    
                        call remove()
                        call DestroyTextTag(tag_p)
                        set tag_p = null
                        set tagPointerParent_p[tagPointer_p].tagPointer_p = 0
                    else
                        set localRemaining = tagPointer_p - 1
                    endif
                else
                    set recyclerPointer[0] = tagPointer_p
                endif
                set tagPointerParent_p[tagPointer_p] = this
                set tag_p = CreateTextTag()
            else
                set tagPointer_p = 0
                set tag_p = null
            endif

So with that, you have local texttags as requested w/o desyncs. That is the only local block in the entire resource.
 
@PurgeAndFire111 :

I don't get what you want mean, opening a thread in a local block wouldn't cause a desync ? (ForForce opens a new thread)
And anyway i'm lost with your suggestion of a force usage, why you would need this ?

You don't use ForForce with it. You just use if IsPlayerInForce(GetLocalPlayer(), f) and then type the code that you want to be performed locally. It will execute them only for the players within force f.
 
Not sure what you mean. You use IsPlayerInForce so that you can perform it locally for multiple players if you want to, with a low level of complexity. The direct method of GetLocalPlayer() == p is only for 1 player. The reason why I suggested IsPlayerInForce is that it has more options for what players you want to execute the actions for. ;)
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You don't get it.

JASS:
scope Sample initializer init

    globals
        private boolean Is_for_player = false
    endglobals

    private function init takes nothing returns nothing
        local player p = GetLocalPlayer()

        set Is_for_player = ( p == Player(1) or p == Player(3) )

        if Is_for_player then // local block only for Player(1) and Player(3)
 
        endif
    endfunction

endscope

That's why Nestharus uses a boolean argument, as it can be used for several players as well, the reason why i suggested a player argument instead, is because you rarely need a local block for X players, rather than for one player only, and it makes more sense than a boolean argument.
He could even create a second method for that.

Now, i suppose that only few people are aware about this, maybe that's why you are talking about complexity.
 
Last edited:
Oh, true I suppose you can do that as well. Although, it is all the same. I just thought inputting a force would be more convenient for the user, but I guess they have to add their own players anyway so a boolean would be more convenient.

So yeah a boolean would be better. I didn't read the entire thread so I just offered an option. :p
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
No problem, nothing in the post mentionned it, you had to figure it by yourself.

Just to be clear :

Instead of ForceAddPlayer :

JASS:
if GetLocalPlayer() == yourPlayer then
    set Your_boolean = true
endif

Instead of ForceRemovePlayer :

JASS:
if GetLocalPlayer() == yourPlayer then
    set Your_boolean = false
endif

Ofc there are room for improvements, like using a global player variable setted at map init instead of GetLocalPlayer, or you could even use a boolean array :

JASS:
library LocalHelp initializer init

    globals
        integer Local_id
        player Local_p
        string Local_s
        boolean array Is_local_player_id // excuse the name :p
    endglobals
    
    private function init takes nothing returns nothing
        set Local_p = GetLocalPlayer()
        set Local_id = GetPlayerId(Local_p)
        set Local_s = I2S(Local_id)
        set Is_local_player_id[Local_id] = true
    endfunction
endlibrary
 
Level 5
Joined
Oct 13, 2010
Messages
84
Hey, Nes. You forgot something.

When i used .setVelocity() and .angle=, i recognized that the angle you made is Radian not Degree.
At least, add a comment about it or add *bj_DEGTORAD when calculating because "angle" makes everybody to think about degree

Edit: btw, can you "open a hole" at method update for change texttag overtime ????
 
Last edited:
btw, can you "open a hole" at method update for change texttag overtime ????

That feature does not belong in this resource. If you want that, code another resource that supports changing texttags over time that uses this one or start your own timers.

I only supported what are supported by current texttags and a tiny bit more (the set texttag target position should lock to that position).

When i used .setVelocity() and .angle= , i recognized that the angle you made is Radian not Degree.
At least, add a comment about it or add *bj_DEGTORAD when calculating because "angle" makes everybody to think about degree

radians are what the rest of the world uses by default.
 
Level 5
Joined
Oct 13, 2010
Messages
84
I found a weird bug:


Error
Code:
lifespan       = 3.0
creating period= 0.5

lifespan       = 3.5
creating period= 0.5

lifespan       = 2.4
creating period= 0.4

Non Error
Code:
lifespan       = 2.5
creating period= 0.5

lifespan       = 3.1
creating period= 0.5

lifespan       = 2.2
creating period= 0.4


JASS:
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
local TextTag tt
 set tt= TextTag.createLocal(GetLocalPlayer()== Player(0))
 call tt.setPosition(GetRectCenterX(bj_mapInitialPlayableArea), GetRectCenterY(bj_mapInitialPlayableArea),70)
 call tt.setText("ABC",12)
 call tt.setVelocity(80., 90.)
 set tt.lifespan=3.0
 set tt.fadepoint=1.1
 endfunction

function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Untitled_Trigger_001, 0.5 )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
 
Last edited:
Level 5
Joined
Oct 13, 2010
Messages
84
When creating texttags overtime like "Example - error case" with this system

I saw:
- First: have a texttag disappears when the time comes !!!
- Second: some texttags disappear when they do not expire !? (i used fadepoint to confirm that)
- Third: texttags disappear like a disease

You should run my test code to see what is the bug.....
I think the bug relates with timer!

For tester 's sake: Map with The bug
 

Attachments

  • Texttag_bugdemo.w3x
    58.3 KB · Views: 65
Top