- Joined
- May 9, 2014
- Messages
- 1,819
Updated to v.1.3.0.5:
- API description has been slightly improved
- Included a method operator height2
- API description has been slightly improved
- Included a method operator height2
method setPos?
GetLocalPlayer()
could be a global/static, assigned once onInit.instanceCheck
is a bad timer name.instancePaused
does also speak for all player textags
- Why is a texttag destroyed and recreated in
method setPos?
SetTextTagVelocity.
(I tried to mimic recycling of text tags). I tried some vatiations and ended up recreating the text tag as a solution.
- Changing a texttag's position should decouple the texttag from a binded unit.
- Document all operators, the ones that do only return are missing...
- Some API is completly missing.
GetLocalPlayer()
could be a global/static, assigned once onInit.
instanceCheck
is a bad timer name.
- Why not attaching pIndex to the timer?
instancePaused
does also speak for all player textags
MyPad
I have a problem, I used your library to create the text and when compiling got the error "Undeclared variable y" . I can send text of what is, can you help.
"call CreateTextTagBJ (Player(0),GetUnitX(GetAttacker()),GetUnitY(GetAttacker()),70,255,0,255, R2S (damage))"
// These textmacros generate a module for the library to use.
//! runtextmacro link_module("local", "private")
//! runtextmacro link_module("non_perma", "private")
scope DebugTextTag initializer Init
private function Cond takes nothing returns nothing
local string s = GetEventPlayerChatString()
local string s1 = SubString(s, 5, StringLength(s))
local integer i = S2I(s1)
local TextTag tt
if i <= 0 then
call BJDebugMsg("Exception: Text Tag cannot be generated by this trigger. (Parameter i is less than 0).")
else
if i > bj_MAX_PLAYER_SLOTS then
call BJDebugMsg("Creating text tag for all!")
else
call BJDebugMsg("Creating text tag for Player " + I2S(i))
endif
//call CreateTextTagBJ(Player(i - 1), 0, 0, 0, 0, 255, 255, "some string")
set tt = TextTag.create(i)
set tt.Red = 0
set tt.Blue = 255
set tt.Green = 255
set tt.Alpha = 0
set tt.duration = TextTag.DEFAULT_DURATION
set tt.fade = TextTag.DEFAULT_DURATION - 0.5
call tt.setPos(0, 0, 0)
call tt.setVelocEx(TextTag.DEFAULT_VELOC_SPEED, TextTag.DEFAULT_VELOC_ANGLE)
set tt.permanent = false
set tt.height = TextTag.DEFAULT_HEIGHT
set tt.message = "Good old burn...."
call tt.setPos(0, 0, 0)
//call tt.visible_to_force(GetForceOfPlayer(Player(0)), true)
if vj_lastCreatedTextTag != -1 then
call BJDebugMsg("Text tag successfully created!")
endif
call BJDebugMsg("Text tag instance created: " + I2S(vj_lastCreatedTextTag))
endif
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-tag", false)
call TriggerAddCondition(t, function Cond)
set t = null
endfunction
endscope
library TextTag requires /*
*/ AllocationAndLinks, /* https://www.hiveworkshop.com/threads/allocation-and-linked-list-bundle.293621/#post-3159130
* - Instantiates an allocator and linked list for the TextTag
*
*/ optional Table /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/
* - Allows binding of the global timer instances.
*/
//==========================//
// TextTag //
// - By MyPad //
// //
// Version: //
// v.1.3.06 //
//==========================//
// Description: //
//======================================================================================//
// TextTag is a library that provides convenience for the creation of local text tags. //
// Thus, it is lightweight in usage, heavy in code, and grants the user a bit of ease //
// in coding. //
//======================================================================================//
// API (Application Programming Interface) //
// Note: Section is in vJASS... but the description slowly starts to resemble Wurst //
//! novjass
struct TextTag
Creation and destruction:
TextTag.create(integer playerId) -> TextTag
// Creates a local texttag for a certain player.
// Note that the playerId parameter takes the actual number of the player,
// not the player itself
// To create a global instance, request 16 or higher (Must be greater than or equal to bj_MAX_PLAYER_SLOTS)
local TextTag tag = TextTag.create(integer playerId) -> TextTag
// Creates a TextTag instance.
..
tag.destroy()
// This destroys a TextTag instance. How it manages to destroy an instance
// for a player is determined durationally or explicitly.
method operator/s:
tag.height2 == real z_coord
// Returns the member z_coord
tag.height2 = real height
// Internally calls method setPos()
tag.height == real size
// Returns the member size
tag.height = real size
// sets the size of a Text Tag
tag.permanent == boolean isPermanent
// Returns the member isPermanent
tag.permanent = boolean bool
// Sets the permanence of a TextTag
tag.duration == real dur
// Returns the member dur -> tells duration.
tag.duration = real newDur
// If it is not permanent, a TextTag's duration is reset to the requested (r).
tag.msg or tag.message == string s
// message is just an abstraction of msg, a method operator that returns a member s.
tag.msg = string sometext
tag.message = string sometext
// Sets the message of a texttag
tag.Red = integer int (0 - 255)
tag.Blue = integer int (0 - 255)
tag.Green = integer int (0 - 255)
tag.Alpha = integer int (0 - 255)
// Returns the red, green, blue, and alpha properties of the text tag.
// Wrapper method operators for above
tag.red = integer int(0 - 255)
tag.green = integer int(0 - 255)
tag.blue = integer int(0 - 255)
tag.alpha = integer int(0 - 255)
// Alpha levels determine how visible is the locally created text tag.
// 255 means the instance is very visible while 0 means not visible.
tag.widget = widget wid
tag.unit = unit uni
// Attaches a text tag to a widget or a unit.
method /(s):
//tag
tag.setPosPrime(real x, real y, real heightOffset, boolean break)
// Sets the position of a TextTag instance to the requested coordinates
// with a z-offset denoted as heightOffset
tag.setPos(real x, real y, real heightOffset)
// Calls setPosPrime with a false parameter for variable break.
tag.setVeloc(real xvel, real yvel)
// Sets the pace at which the tag moves at a certain direction.
tag.setVelocEx(real speed, real angle)
// Wrapper function for setVeloc
tag.generate(integer playerId) -> TextTag
// Generates the same text tag for another player
tag.transfer(integer playerId) -> TextTag
// Same as generate, but destroys the base instance.
tag.visible_to_force(force f, boolean b)
// Only works when the Text Tag instance is a global one.
// Sets the visibility of a global text tag to flag b,
// towards players belonging to a certain force f
endstruct
function CreateTextTagBJ(player p, real x, real y, real offset, integer red, integer green, integer blue, string msg) {TextTag} // or returns TextTag, -> TextTag
// Creates a TextTag instance with the default settings.
// If not going to use a variable, the snippet returns a global variable "vj_lastCreatedTextTag"
{vj_lastCreatedTextTag} // or return vj_lastCreatedTextTag
//! endnovjass
//! novjass
Update History:
v.1.3.0.6 - Added some details on method operator getters. (Denoted by ==)
- Fixed setVeloc-s slightly buggy behavior. (It didn-t originally pause the texttag when set to 0)
- Exposed a new method setPosPrime (does what setPos did before, with a boolean parameter)
- setPos now calls setPosPrime with a false flag.
v.1.3.0.5 - Improved API documentation.
- Added a new method operator height2
- Backwards compatible!
v.1.3.0.4 - Update history created!
- method operator unit= fixed.
//! endnovjass
globals
// This determines the check rate of the timers.
private constant real INTERVAL = 1/32.
endglobals
// These textmacros generate a module for the library to use.
///! runtextmacro link_module("local", "private")
///! runtextmacro link_module("non_perma", "private")
//! runtextmacro DLinkedListT("local", "private")
//! runtextmacro DLinkedListT("non_perma", "private")
private module TextTag_Mod
// How many texttags are possible per head node list
// Defined to be (number of desired text tags + 1)
readonly static constant integer ALLOC_MULTIPLIER = 101
// These are the default settings for heights
// Don't touch unless you know what you are doing
readonly static constant real DEFAULT_HEIGHT = 10.
readonly static constant real HEIGHT_OFFSET = 0.023 / 10.
// Default offset value for velocity
readonly static constant real VELOCITY_OFFSET = 0.071 / 128.
// Default duration of a texttag instance
readonly static constant real DEFAULT_DURATION = 2.
// Default speed and angle of a texttag
readonly static constant real DEFAULT_VELOC_SPEED = 80.
readonly static constant real DEFAULT_VELOC_ANGLE = 90.
// The list of deallocated instances.
readonly thistype recCount
readonly integer tag_count
// In favor of using an AllocationAndLinks library, I had just reduced the text into implementing a locally
// generated module..
implement DoubleLink_local
// A linked list for non permanent text tags.
implement DoubleLink_non_perma
// This member can only be written to the head nodes. It is best to not refer to it as it only works for
// head nodes.
boolean instancePaused
// The timer generated only exists for head nodes. They do not exist anywhere else, thus the readonly
// behaviour. Likewise, instanceData behaves in the same manner.
readonly timer timerChecker
readonly integer instanceData
method deallocate takes nothing returns nothing
local integer pIndex = this/ALLOC_MULTIPLIER
set recCount = thistype(pIndex*ALLOC_MULTIPLIER).recCount
set thistype(pIndex*ALLOC_MULTIPLIER).recCount = this
endmethod
// The allocation scheme is quite different, having head nodes (integer playerId * ALLOC_MULTIPLIER)
static method allocate takes integer pIndex returns thistype
local thistype this = thistype(pIndex*ALLOC_MULTIPLIER).recCount
// The if-statement checks if the next instance is to be recycled or generated
if this.recCount == pIndex*ALLOC_MULTIPLIER then
// We make sure that the instance does not encroach on another head node's instance count.
// So, Player 1 has a head node of 0, Player 2 has it at 101, Player 3 has it at 202, and so on...
if integer(this) < (pIndex+1)*ALLOC_MULTIPLIER then
// From indices pIndex*ALLOC_MULTIPLIER to (pIndex+1)*ALLOC_MULTIPLIER - 1
set this = this + 1
set thistype(pIndex*ALLOC_MULTIPLIER).recCount = this
set this.recCount = pIndex*ALLOC_MULTIPLIER
else
// Error, text tag count exceeded...
set this = -1
endif
else
// Recycling; it is impossible for the allocate method to go over the max.
set thistype(pIndex*ALLOC_MULTIPLIER).recCount = this.recCount
set this.recCount = pIndex*ALLOC_MULTIPLIER
endif
return this
endmethod
// Add a Table instance if it exists
static if LIBRARY_Table then
readonly static Table timerData = 0
endif
private static method onInit takes nothing returns nothing
local thistype this = 0
static if LIBRARY_Table then
set thistype.timerData = Table.create()
endif
loop
exitwhen integer(this) > bj_MAX_PLAYER_SLOTS
// These are the head nodes that are going to be initialized
// Push them first as heads of their respective lists.
call thistype(this*ALLOC_MULTIPLIER).insert(this*ALLOC_MULTIPLIER)
call thistype(this*ALLOC_MULTIPLIER).non_perma_insert(this*ALLOC_MULTIPLIER)
set thistype(this*ALLOC_MULTIPLIER).head = this*ALLOC_MULTIPLIER
set thistype(this*ALLOC_MULTIPLIER).non_perma_head = this*ALLOC_MULTIPLIER
set thistype(this*ALLOC_MULTIPLIER).recCount = this*ALLOC_MULTIPLIER
set thistype(this*ALLOC_MULTIPLIER).timerChecker = CreateTimer()
set thistype(this*ALLOC_MULTIPLIER).instanceData = this*ALLOC_MULTIPLIER
set thistype(this*ALLOC_MULTIPLIER).instancePaused = true
static if LIBRARY_Table then
set timerData.integer[GetHandleId(thistype(this*ALLOC_MULTIPLIER).timerChecker)] = this*ALLOC_MULTIPLIER
endif
set this = this + 1
endloop
endmethod
endmodule
struct TextTag extends array
private texttag texttag
private widget wid_targ
private unit uni_targ
private boolean isPermanent
private real dur
private string s
private real size
private real fadepoint
private integer r
private integer g
private integer b
private integer a
private real d_xvel
private real d_yvel
readonly real tag_x
readonly real tag_y
readonly real tag_z
implement DoubleLink
implement TextTag_Mod
// This method simply gets the player id through division.
private method get_player_id takes nothing returns integer
if this - this/ALLOC_MULTIPLIER*ALLOC_MULTIPLIER == 0 then
// The instance is a head node.
return -1
endif
return this/ALLOC_MULTIPLIER
endmethod
method destroy takes nothing returns nothing
local thistype that = this.local_next
local integer pIndex = get_player_id()
if pIndex >= bj_MAX_PLAYER_SLOTS then
call DestroyTextTag(texttag)
set texttag = null
else
if GetLocalPlayer() == Player(pIndex) then
call DestroyTextTag(texttag)
set texttag = null
endif
endif
if that != 0 then
loop
exitwhen that == this
call that.local_pop()
call that.deallocate()
set that = that.local_next
endloop
endif
if non_perma_head != 0 then
call non_perma_pop()
set non_perma_head = 0
endif
set wid_targ = null
set uni_targ = null
set isPermanent = false
set dur = 0.
set r = 0
set g = 0
set b = 0
set a = 0
set d_xvel = 0
set d_yvel = 0
set tag_x = 0
set tag_y = 0
set s = ""
set head = 0
call pop()
call deallocate()
set thistype(pIndex*ALLOC_MULTIPLIER).tag_count = thistype(pIndex*ALLOC_MULTIPLIER).tag_count - 1
endmethod
//============================================================//
// Methods //
//============================================================//
method setPosPrime takes real x, real y, real heightOffset, boolean break returns nothing
// pIndex is configured to each player
// Example:
// If this is 99, then the instance belongs to the list of Player 1.
// or Player(0)
// If this is 102, then the instance belongs to the list of Player 2.
// or Player(1)
// If this is 202, then the
local integer pIndex = get_player_id()
if break then
set uni_targ = null
set wid_targ = null
endif
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set tag_x = x
set tag_y = y
if uni_targ == null then
set tag_z = heightOffset
else
set tag_z = heightOffset - GetUnitFlyHeight(uni_targ)
endif
if pIndex >= bj_MAX_PLAYER_SLOTS then
if d_xvel != 0 or d_yvel != 0 then
call DestroyTextTag(texttag)
set texttag = CreateTextTag()
call SetTextTagPos(texttag, x, y, heightOffset)
call SetTextTagText(texttag, s, size * HEIGHT_OFFSET)
call SetTextTagColor(texttag, r, g, b, a)
call SetTextTagPermanent(texttag, isPermanent)
if not isPermanent then
call SetTextTagLifespan(texttag, dur)
call SetTextTagFadepoint(texttag, RMaxBJ(fadepoint, 0))
endif
else
call SetTextTagPos(texttag, x, y, heightOffset)
endif
else
if d_xvel != 0 or d_yvel != 0 then
if GetLocalPlayer() == Player(pIndex) then
call DestroyTextTag(texttag)
set texttag = CreateTextTag()
call SetTextTagPos(texttag, x, y, heightOffset)
call SetTextTagText(texttag, s, size * HEIGHT_OFFSET)
call SetTextTagColor(texttag, r, g, b, a)
call SetTextTagPermanent(texttag, isPermanent)
if not isPermanent then
call SetTextTagLifespan(texttag, dur)
call SetTextTagFadepoint(texttag, RMaxBJ(fadepoint, 0))
endif
endif
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagPos(texttag, x, y, heightOffset)
endif
endif
endif
endmethod
method setPos takes real x, real y, real heightOffset returns nothing
call setPosPrime(x, y, heightOffset, false)
endmethod
method setVeloc takes real xvel, real yvel returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
if pIndex >= bj_MAX_PLAYER_SLOTS then
if xvel != 0 or yvel != 0 then
call SetTextTagVelocity(texttag, xvel * VELOCITY_OFFSET, yvel * VELOCITY_OFFSET)
else
call setPos(tag_x, tag_y, 0)
endif
else
if xvel != 0 or yvel != 0 then
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagVelocity(texttag, xvel * VELOCITY_OFFSET, yvel * VELOCITY_OFFSET)
endif
else
call setPos(tag_x, tag_y, 0)
endif
endif
set d_xvel = xvel
set d_yvel = yvel
endmethod
method setVelocEx takes real speed, real angle returns nothing
call setVeloc(Cos(angle * bj_DEGTORAD) * speed, Sin(angle * bj_DEGTORAD) * speed)
endmethod
// Sets visibility of a global text tag to a certain flag towards a certain force.
method visible_to_force takes force f, boolean b returns nothing
local integer pIndex = get_player_id()
if pIndex >= bj_MAX_PLAYER_SLOTS then
if IsPlayerInForce(GetLocalPlayer(), f) then
call SetTextTagVisibility(texttag, b)
endif
debug else
debug call BJDebugMsg("thistype( " + I2S(this) + ".setVelocEx: Cannot modify visibility of a local text tag.")
endif
endmethod
//============================================================//
// Method operators //
//============================================================//
method operator permanent takes nothing returns boolean
return isPermanent
endmethod
method operator permanent= takes boolean b returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
if not b then
if this.non_perma_head == 0 then
call this.non_perma_insert(this.head)
set this.non_perma_head = this.head
endif
else
if this.non_perma_head != 0 then
call this.non_perma_pop()
set this.non_perma_head = 0
endif
endif
set isPermanent = b
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagPermanent(texttag, isPermanent)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagPermanent(texttag, isPermanent)
endif
endif
endmethod
method operator duration takes nothing returns real
if not isPermanent then
return dur
endif
// If permanent, the text tag will always endure. Thus, its' duration is -1
return -1.
endmethod
method operator duration= takes real r returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
// Can't set a duration to permanent text tags.
if isPermanent then
return
endif
set dur = r
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagLifespan(texttag, dur)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagLifespan(texttag, dur)
endif
endif
endmethod
method operator msg takes nothing returns string
return s
endmethod
method operator msg= takes string str returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set s = str
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagText(texttag, s, size * HEIGHT_OFFSET)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagText(texttag, s, size * HEIGHT_OFFSET)
endif
endif
endmethod
// These methods are wrappers...
method operator message takes nothing returns string
return s
endmethod
method operator message= takes string str returns nothing
set msg = str
endmethod
// A newer method operator that actually returns the height of the text tag.
method operator height2 takes nothing returns real
return tag_z
endmethod
method operator height2= takes real new returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
if wid_targ != null then
call setPos(GetWidgetX(wid_targ), GetWidgetY(wid_targ), new)
elseif uni_targ != null then
call setPos(GetUnitX(uni_targ), GetUnitY(uni_targ), new)
else
call setPos(tag_x, tag_y, new)
endif
endmethod
// Cannot deal away with backwards compatibility here..
method operator height takes nothing returns real
return size
endmethod
method operator height= takes real h returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set size = h
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagText(texttag, s, size * HEIGHT_OFFSET)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagText(texttag, s, size * HEIGHT_OFFSET)
endif
endif
endmethod
method operator fade takes nothing returns real
return fadepoint
endmethod
method operator fade= takes real fader returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
if not isPermanent then
set fadepoint = RMinBJ(RAbsBJ(fader), dur)
else
return
endif
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagFadepoint(texttag, fadepoint)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagFadepoint(texttag, fadepoint)
endif
endif
endmethod
method operator Red takes nothing returns integer
return r
endmethod
method operator Red= takes integer r returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set this.r = r
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagColor(texttag, this.r, g, b, 255 - a)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagColor(texttag, this.r, g, b, 255 - a)
endif
endif
endmethod
// Wrappers for wrappers
method operator red takes nothing returns integer
return r
endmethod
method operator red= takes integer r returns nothing
set Red = r
endmethod
method operator Green takes nothing returns integer
return g
endmethod
method operator Green= takes integer g returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set this.g = g
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagColor(texttag, r, this.g, b, 255 - a)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagColor(texttag, r, this.g, b, 255 - a)
endif
endif
endmethod
method operator green takes nothing returns integer
return g
endmethod
method operator green= takes integer g returns nothing
set Green = g
endmethod
method operator Blue takes nothing returns integer
return b
endmethod
method operator Blue= takes integer b returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set this.b = b
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagColor(texttag, r, g, this.b, 255 - a)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagColor(texttag, r, g, this.b, 255 - a)
endif
endif
endmethod
method operator blue takes nothing returns integer
return b
endmethod
method operator blue= takes integer b returns nothing
set Blue = b
endmethod
method operator Alpha takes nothing returns integer
return a
endmethod
method operator Alpha= takes integer a returns nothing
local integer pIndex = get_player_id()
debug if pIndex < 0 then
debug call BJDebugMsg("The parameter this is a head node.")
debug return
debug endif
set this.b = b
if pIndex >= bj_MAX_PLAYER_SLOTS then
call SetTextTagColor(texttag, r, g, b, 255 - this.a)
else
if GetLocalPlayer() == Player(pIndex) then
call SetTextTagColor(texttag, r, g, b, 255 - this.a)
endif
endif
endmethod
method operator alpha takes nothing returns integer
return a
endmethod
method operator alpha= takes integer a returns nothing
set Alpha = a
endmethod
method operator widget takes nothing returns widget
return wid_targ
endmethod
method operator widget= takes widget wid returns nothing
if wid == null then
debug call BJDebugMsg("Warning: the parameter wid is null.")
// Must not attach to null instance...
return
endif
set uni_targ = null
set wid_targ = wid
call setVeloc(0,0)
call setPos(GetWidgetX(wid_targ), GetWidgetY(wid_targ), tag_z)
endmethod
method operator unit takes nothing returns unit
return uni_targ
endmethod
method operator unit= takes unit uni returns nothing
if uni == null then
debug call BJDebugMsg("Warning: the parameter uni is null.")
// Must not attach to null instance...
return
endif
set wid_targ = null
set uni_targ = uni
call setVeloc(0,0)
call setPos(GetUnitX(uni_targ), GetUnitY(uni_targ), GetUnitFlyHeight(uni_targ))
endmethod
// ================================= //
// Create and onTick methods //
// ================================= //
// A linear search specifically for the onTick static method
private static method timer_search takes timer t returns thistype
static if LIBRARY_Table then
return TextTag.timerData.integer[GetHandleId(t)]
else
local thistype this = 0
loop
exitwhen timerChecker == t
if integer(this) > bj_MAX_PLAYER_SLOTS*ALLOC_MULTIPLIER then
set this = -1
exitwhen true
endif
set this = this + ALLOC_MULTIPLIER
endloop
return this
endif
endmethod
private static method onTick takes nothing returns nothing
local timer tick = GetExpiredTimer()
local integer pIndex
local thistype head
local thistype this
// Gets the head node
set pIndex = timer_search(tick)
set head = pIndex
set this = head.next
static if LIBRARY_Table then
if pIndex == 0 then
call BJDebugMsg("thistype.onTick: pIndex is invalid!")
endif
endif
if this == head then
set instancePaused = true
call PauseTimer(tick)
endif
// Loop over all instances related to the timer
// Each timer has a list for each player.
loop
exitwhen this == head
if this.uni_targ != null or this.wid_targ != null then
if this.wid_targ != null then
set this.widget = wid_targ
else
set this.unit = uni_targ
endif
else
set this.tag_x = this.tag_x + d_xvel * INTERVAL
set this.tag_y = this.tag_y + d_yvel * INTERVAL
endif
if not this.isPermanent then
set this.dur = this.dur - INTERVAL
set this.fade = this.fade - INTERVAL
if this.dur <= 0. then
call this.destroy()
endif
endif
set this = this.next
endloop
endmethod
// A return-safe way to search for non-permanent text tags.
private static method non_perma_search takes thistype pIndex returns thistype
local thistype this = thistype(pIndex*ALLOC_MULTIPLIER).non_perma_next
if this == thistype(pIndex*ALLOC_MULTIPLIER) then
set this = -1
endif
return this
endmethod
static method create takes integer pIndex returns thistype
local thistype this = 0
local integer i = 0
local thistype array that
if pIndex > bj_MAX_PLAYER_SLOTS then
set pIndex = bj_MAX_PLAYER_SLOTS
elseif pIndex < 0 then
// We make sure that the instance falls under the list
set pIndex = ModuloInteger(pIndex, bj_MAX_PLAYER_SLOTS)
endif
if pIndex == bj_MAX_PLAYER_SLOTS then
if thistype(pIndex*ALLOC_MULTIPLIER).tag_count == ALLOC_MULTIPLIER - 1 then
// A very unlikely case where all 1717 instances are all occupied at the same time.
set this = non_perma_search(pIndex)
debug if this == -1 then
debug call BJDebugMsg("No temporary text tag instances remaining!")
debug set this = 1/0
debug endif
// We move the instance to the end of the list.
call this.pop()
call this.insert(pIndex*ALLOC_MULTIPLIER)
// We move the non-permanent instance to the end of the list.
call this.non_perma_pop()
call this.non_perma_insert(pIndex*ALLOC_MULTIPLIER)
else
// We check if all local players can afford to have another text tag.
loop
// We stop when variable i is greater than or equal to bj_MAX_PLAYER_SLOTS
// When that happens, our variable can be globally generated.
exitwhen i >= bj_MAX_PLAYER_SLOTS
set that[i] = allocate(i)
// If any instance returns -1, then we stop looping and no longer proceed with creating
// a text tag
if that[i] == -1 then
debug call BJDebugMsg("Breakpoint found: Player (" + I2S(i + 1) + ") cannot have any more text tags.")
exitwhen true
endif
set i = i + 1
endloop
if i < bj_MAX_PLAYER_SLOTS then
// Creation failed, sadly...
set this = -1
loop
exitwhen i < 0
// We deallocate them as they were temporarily requested...
call that[i].deallocate()
set i = i - 1
endloop
else
// Creation success..
set this = allocate(pIndex)
call this.insert(pIndex*ALLOC_MULTIPLIER)
set this.head = pIndex*ALLOC_MULTIPLIER
call this.local_insert(this)
set this.local_head = this
set this.texttag = CreateTextTag()
set thistype(pIndex*ALLOC_MULTIPLIER).tag_count = thistype(pIndex*ALLOC_MULTIPLIER).tag_count + 1
set i = i - 1
loop
exitwhen i < 0
// We link the local instances to the global instance
call that[i].local_insert(this)
set that[i].local_head = this
set i = i - 1
endloop
if thistype(pIndex*ALLOC_MULTIPLIER).instancePaused then
set thistype(pIndex*ALLOC_MULTIPLIER).instancePaused = false
call TimerStart(thistype(pIndex*ALLOC_MULTIPLIER).timerChecker, INTERVAL, true, function thistype.onTick)
endif
// Assume that the text tag is not permanent.
call this.non_perma_insert(pIndex*ALLOC_MULTIPLIER)
// This is done so as to not cause any logical errors when looping through the non_permanent list
// By setting it to the next head node, we avoid any false positive checks with player 1.
set this.non_perma_head = (pIndex+1)*ALLOC_MULTIPLIER
endif
endif
else
// How it is done:
// Every player knows that a text tag exists.
// However, only one player can actually see what the text tag really is.
// AS requested by Xonok...
// If the tag count of the head node is 100, then search for a non-permanent instance
// Override if return success!
if thistype(pIndex*ALLOC_MULTIPLIER).tag_count == ALLOC_MULTIPLIER - 1 then
set this = non_perma_search(pIndex)
debug if this == -1 then
debug call BJDebugMsg("No temporary text tag instances remaining!")
debug set this = 1/0
debug endif
// We move the instance to the end of the list.
call this.pop()
call this.insert(pIndex*ALLOC_MULTIPLIER)
// We move the non-permanent instance to the end of the list.
call this.non_perma_pop()
call this.non_perma_insert(pIndex*ALLOC_MULTIPLIER)
else
set this = allocate(pIndex)
if this != -1 then
// Allocation Successful..
// Update the number of text tags
set thistype(pIndex*ALLOC_MULTIPLIER).tag_count = thistype(pIndex*ALLOC_MULTIPLIER).tag_count + 1
// Create a link to the iterator list for checking of expiration of text tag.
call this.insert(pIndex*ALLOC_MULTIPLIER)
set this.head = pIndex*ALLOC_MULTIPLIER
if GetLocalPlayer() == Player(pIndex) then
set this.texttag = CreateTextTag()
endif
if thistype(pIndex*ALLOC_MULTIPLIER).instancePaused then
set thistype(pIndex*ALLOC_MULTIPLIER).instancePaused = false
call TimerStart(thistype(pIndex*ALLOC_MULTIPLIER).timerChecker, INTERVAL, true, function thistype.onTick)
endif
call this.non_perma_insert(pIndex*ALLOC_MULTIPLIER)
set this.non_perma_head = (pIndex+1)*ALLOC_MULTIPLIER
endif
endif
endif
return this
endmethod
// Copies a Text Tag instance to another player
method generate takes integer playerId returns thistype
local thistype new = 0
local integer pIndex = get_player_id()
if pIndex == playerId then
debug call BJDebugMsg("|cffffcc00(method generate)|r; |cffff0000Error:|r playerId instance provided is the same as pIndex")
debug call BJDebugMsg("|cffffcc00(method generate)|r; value of pIndex: " + I2S(pIndex))
return this
endif
set new = create(playerId)
if new == -1 then
debug call BJDebugMsg("|cffffcc00(method generate)|r; |cffffcc00Exception:|r instance count exceeded. (Too many permanent text tags.)")
return new
endif
set new.duration = duration
set new.fade = fade
call new.setPos(tag_x, tag_y, 0)
call new.setVeloc(tag_x, tag_y)
set new.permanent = permanent
set new.height = height
set new.Red = red
set new.Green = green
set new.Blue = blue
set new.Alpha = alpha
set new.msg = msg
return new
endmethod
// Same as the method generate but destroys the base instance
method transfer takes integer playerId returns thistype
local thistype that = generate(playerId)
if that == -1 then
debug call BJDebugMsg("|cffffcc00(method transfer)|r; |cffff0000Exception:|r delegate method called failed.")
return this
endif
call destroy()
return that
endmethod
endstruct
globals
TextTag vj_lastCreatedTextTag = 0
endglobals
function CreateTextTagBJ takes player p, real x, real y, real offset, integer red, integer green, integer blue, string msg returns TextTag
local TextTag this
if p == null then
set this = TextTag.create(bj_MAX_PLAYER_SLOTS)
else
set this = TextTag.create(GetPlayerId(p))
endif
if this == -1 then
return this
endif
set this.duration = TextTag.DEFAULT_DURATION
set this.fade = TextTag.DEFAULT_DURATION - 0.5
call this.setPos(x, y, offset)
call this.setVelocEx(TextTag.DEFAULT_VELOC_SPEED, TextTag.DEFAULT_VELOC_ANGLE)
set this.permanent = false
set this.height = TextTag.DEFAULT_HEIGHT
set this.Red = red
set this.Green = green
set this.Blue = blue
set this.Alpha = 0
set this.msg = msg
set vj_lastCreatedTextTag = this
return vj_lastCreatedTextTag
endfunction
endlibrary
I don't think I released a later version of this as another resource. However, I did forget to mention that the current version of the library has been changed to 1.4.00 at some point (my mistake there).When you say you've released an update, did you submit that as a separate resource?
Hmm, after looking at the library now, I forgot that handle IDs will not always be contiguous, so the possibility of a collision of indices may occur.Do you intend to update this at any point? The ModuloInteger on the timer handle ID is creative, but it can potentially cause overlapping issues with the index (outside of a vaccuum test map of course).
GetHashID
now generates wholly unique indices for the internal timers used by the system.