![]() |
#1 |
Procrastination Incarnate
Development Director
|
![]() A short script that allows you to get the current vertex color and scale of units.
__________________Requires ARGB, Table and optionally AutoIndex. Credit where credit is due: a library like this was first proposed by Tot here, but didn't merit an approval at the time. It was a useful idea, though, I had a use for it myself and since Tot doesn't appear to be active anymore, I decided to rework and submit it. ![]() library UnitAppearanceTracker requires ARGB, Table, optional AutoIndex //***************************************************************** //* UNIT APPEARANCE TRACKER //* //* written by: Anitarf //* original implementation: Tot //* requires: -ARGB //* -Table //* optional: -AutoIndex //* //* There is no native way to get a unit's current vertex colour //* or scale in WC3. This library was written to work around this //* limitation. The library tracks changes made to the vertex //* colour and scale of individual units so that those values can //* be obtained for a specific unit at any time. Vertex color is //* described using ARGB. This library now also tracks the //* animation speed of units. //* //* local ARGB c = GetUnitVertexColor(u) //* local real s = GetUnitScale(u) //* local real s = GetUnitTimeScale(u) //* //* If a unit type has a vertex colour or scale defined in the //* object editor that is different from the DEFAULT_COLOR/SCALE, //* UnitAppearanceTracker must be informed of this using the //* following functions in order for it to correctly assume these //* values for newly created units: //* //* call SetUnitTypeVertexColor('nzom', 0xFFFF9696) // zombie //* call SetUnitTypeScale('hgry', 1.2) // gryphon rider //* //* Finally, to be able to reset a unit's color and scale, the //* library provides the following functions that return the //* default values for a given unit type: //* //* set c = GetUnitTypeVertexColor(GetUnitTypeId(u)) //* set s = GetUnitTypeScale(GetUnitTypeId(u)) //* //* This library optionally requires AutoIndex. Without AutoIndex, //* the library still has full functionality, AutoIndex only makes //* it slightly faster, however it will only be able to track the //* appearance of indexed units. //***************************************************************** globals // Set this to false if you want to track the appearance of units ignored by AutoIndex. private constant boolean USE_AUTOINDEX_IF_PRESENT = true // Multiplication factor used when converting reals to integers so they can be stored in Table. private constant real PRECISION = 1000.0 // You do not need to call SetUnitTypeVertexColor/SetUnitTypeScale for units that use these default values, // so the values should match the most commonly used scale/colour in your map. Speed should always be 1.0. private ARGB DEFAULT_COLOR = 0xFFFFFFFF private real DEFAULT_SCALE = 1.0 private real DEFAULT_SPEED = 1.0 // END OF CALIBRATION SECTION // ================================================================ private ARGB array unitsColor private real array unitsScale private real array unitsSpeed private HandleTable unitColor private HandleTable unitScale private HandleTable unitSpeed private Table unittypeColor private Table unittypeScale endglobals // ================================================================ // Hooks: private function SetUnitVertexColor_hook takes unit whichUnit, integer red, integer green, integer blue, integer alpha returns nothing static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then set unitsColor[GetUnitId(whichUnit)] = ARGB.create(alpha, red, green, blue) else set unitColor[whichUnit] = integer(ARGB.create(alpha, red, green, blue)) endif endfunction private function SetUnitVertexColorBJ_hook takes unit whichUnit, real red, real green, real blue, real transparency returns nothing call SetUnitVertexColor_hook(whichUnit, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency)) endfunction hook SetUnitVertexColor SetUnitVertexColor_hook hook SetUnitVertexColorBJ SetUnitVertexColorBJ_hook // ---------------------------------------------------------------- private function SetUnitScale_hook takes unit whichUnit, real scaleX, real scaleY, real scaleZ returns nothing static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then set unitsScale[GetUnitId(whichUnit)] = scaleX else set unitScale[whichUnit] = R2I(scaleX*PRECISION) endif endfunction private function SetUnitScalePercent_hook takes unit whichUnit, real percentScaleX, real percentScaleY, real percentScaleZ returns nothing call SetUnitScale_hook(whichUnit, percentScaleX * 0.01, 0.0, 0.0) endfunction hook SetUnitScale SetUnitScale_hook hook SetUnitScalePercent SetUnitScalePercent_hook // ---------------------------------------------------------------- private function SetUnitTimeScale_hook takes unit whichUnit, real timeScale returns nothing static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then set unitsSpeed[GetUnitId(whichUnit)] = timeScale else set unitSpeed[whichUnit] = R2I(timeScale*PRECISION) endif endfunction private function SetUnitTimeScalePercent_hook takes unit whichUnit, real percentScale returns nothing call SetUnitTimeScale_hook(whichUnit, percentScale * 0.01) endfunction hook SetUnitTimeScale SetUnitTimeScale_hook hook SetUnitTimeScalePercent SetUnitTimeScalePercent_hook // ================================================================ // User functions: function GetUnitVertexColor takes unit u returns ARGB static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then return unitsColor[GetUnitId(u)] else return ARGB(unitColor[u]) endif endfunction function GetUnitScale takes unit u returns real static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then return unitsScale[GetUnitId(u)] else return unitScale[u]/PRECISION endif endfunction function GetUnitTimeScale takes unit u returns real static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then return unitsSpeed[GetUnitId(u)] else return unitSpeed[u]/PRECISION endif endfunction // ---------------------------------------------------------------- function GetUnitTypeVertexColor takes integer unitID returns ARGB if unittypeColor.exists(unitID) then return ARGB(unittypeColor[unitID]) endif return DEFAULT_COLOR endfunction function GetUnitTypeScale takes integer unitID returns real if unittypeScale.exists(unitID) then return unittypeScale[unitID]/PRECISION endif return DEFAULT_SCALE endfunction // ---------------------------------------------------------------- function SetUnitTypeVertexColor takes integer unitID, ARGB c returns nothing set unittypeColor[unitID]=integer(c) endfunction function SetUnitTypeScale takes integer unitID, real s returns nothing set unittypeScale[unitID]=R2I(s*PRECISION) endfunction // ================================================================ // Initialization: private struct Initializer extends array private static method UnitEntersMap takes unit u returns nothing static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then set unitsScale[GetUnitId(u)] = GetUnitTypeScale(GetUnitTypeId(u)) set unitsColor[GetUnitId(u)] = GetUnitTypeVertexColor(GetUnitTypeId(u)) set unitsSpeed[GetUnitId(u)] = DEFAULT_SPEED else set unitScale[u] = R2I(GetUnitTypeScale(GetUnitTypeId(u))*PRECISION) set unitColor[u] = integer(GetUnitTypeVertexColor(GetUnitTypeId(u))) set unitSpeed[u] = R2I(DEFAULT_SPEED*PRECISION) endif endmethod private static method UnitEntersMapTrigger takes nothing returns boolean call .UnitEntersMap(GetFilterUnit()) return false endmethod // ---------------------------------------------------------------- private static method onInit takes nothing returns nothing local rect rc local region rg local trigger t set unittypeColor=Table.create() set unittypeScale=Table.create() static if LIBRARY_AutoIndex then call OnUnitIndexed(UnitEntersMap) static if not USE_AUTOINDEX_IF_PRESENT then set unitScale=Table.create() set unitColor=Table.create() set unitSpeed=Table.create() endif else set unitScale=Table.create() set unitColor=Table.create() set unitSpeed=Table.create() set rc=GetWorldBounds() set rg=CreateRegion() set t=CreateTrigger() call RegionAddRect(rg, rc) call RemoveRect(rc) call TriggerRegisterEnterRegion( t, rg, Condition(function Initializer.UnitEntersMapTrigger) ) set rc=null set rg=null endif endmethod endstruct endlibrary Last edited by Anitarf : 07-02-2011 at 11:16 AM. |
![]() |
![]() |
Sponsored Links - Login to hide this ad! |
|
![]() |
#2 |
User
Join Date: Apr 2009
Posts: 38
![]() |
![]() Useful. I like it, especially the optional support for AutoIndex, as people also use other indexers.
![]() //* Finally, to be able to reset a unit's color and scale, the //* library provides the followin functions that return the //* default values for a given unit type: I found a mistake here: "Followin". Although one thing I don't understand is why UnitEntersMap is not placed in UnitEntersMapTrigger, instead it's being called. Something like this: ![]() private static method UnitEntersMapTrigger takes nothing returns nothing local unit u = GetTriggerUnit() static if LIBRARY_AutoIndex then set unitsScale[GetUnitId(u)] = GetUnitTypeScale(GetUnitTypeId(u)) set unitsColor[GetUnitId(u)] = GetUnitTypeVertexColor(GetUnitTypeId(u)) else set unitScale[u] = R2I(GetUnitTypeScale(GetUnitTypeId(u))*1000.0) set unitColor[u] = integer(GetUnitTypeVertexColor(GetUnitTypeId(u))) endif set u = null endmethod Was it to avoid the declaration of a local unit? *Shrugs* Also can't GetUnitTypeId(u) be set to a local to avoid double call? Although this is done on initialization, so I guess it's negligible. The same goes for GetUnitId(). ![]() private function SetUnitScalePercent_hook takes unit whichUnit, real percentScaleX, real percentScaleY, real percentScaleZ returns nothing call SetUnitScale_hook(whichUnit, percentScaleX * 0.01, percentScaleY * 0.01, percentScaleZ * 0.01) endfunction scaleY and scaleZ don't work, so you could save two multiplications. I don't know why Blizzard implemented them. ![]() private function SetUnitVertexColorBJ_hook takes unit whichUnit, real red, real green, real blue, real transparency returns nothing call SetUnitVertexColor_hook(whichUnit, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency)) endfunction All those PercentTo255 calls are: ![]() function PercentTo255 takes real percentage returns integer return PercentToInt(percentage, 255) endfunction function PercentToInt takes real percentage, integer max returns integer local integer result = R2I(percentage * I2R(max) * 0.01) if (result < 0) then set result = 0 elseif (result > max) then set result = max endif return result endfunction Or simply: R2I(255 /*(100 for alpha)*/ * I2R(max) * 0.01)? No need to check for max, users shouldn't be setting values to below 0 or above 255 anyways. Last edited by BlackRose : 12-13-2010 at 11:35 AM. |
![]() |
![]() |
![]() |
#3 | ||||||
Procrastination Incarnate
Development Director
|
![]() Quote:
The only reason the AutoIndex requirement is optional is because I don't want libraries to have more requirements than is needed. Since Table can already fulfil the job of attaching data to units, AutoIndex is optionally supported only because it can do the job slightly faster, so there's no sense not to use it if it happens to be in the map already. I suppose I should add that explanation to the documentation. Quote:
Quote:
Quote:
Quote:
Quote:
Thanks for the review. Last edited by Anitarf : 12-13-2010 at 11:24 AM. |
||||||
![]() |
![]() |
![]() |
#4 | ||
User
Join Date: Apr 2009
Posts: 38
![]() |
![]() Quote:
I've always thought there was a clash between such systems, so I guess that's handy information. Quote:
Oops, my bad, yeah, I meant on entering map. I actually realised that R2I(255 /*(100 for alpha)*/ * I2R(max) * 0.01) is incorrect. It should actually be R2I(percentage * 2.55). However you've decided to keep it as PercentTo255, I just wanted to correct myself :) Edit: You could still change those PercentTo255 calls to PercentToInt( percentage, 255 ). That's one less call :) Edit: What is the effect this system can have when trying to modify the vertex colour of a unit to only one player? For example, making a unit's alpha zero for all players except one. Last edited by BlackRose : 02-21-2011 at 11:20 AM. |
||
![]() |
![]() |
![]() |
#5 | |
Procrastination Incarnate
Development Director
|
![]() Sorry, I didn't notice your last edit until now.
__________________Quote:
|
|
![]() |
![]() |
![]() |
#6 |
User
Join Date: Apr 2011
Posts: 6
![]() |
![]() You should include support for TimeScale as well.
Otherwise, this is quite cool. Last edited by Jewel : 04-29-2011 at 11:34 AM. |
![]() |
![]() |
![]() |
#7 | |
Procrastination Incarnate
Development Director
|
![]() Quote:
|
|
![]() |
![]() |
![]() |
#8 |
User
Join Date: May 2011
Posts: 85
![]() ![]() |
![]() ![]() function GetUnitScale takes unit u returns real static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then return unitsScale[GetUnitId(u)] else return unitScale[u]*0.001 endif endfunction ![]() function GetUnitTimeScale takes unit u returns real static if USE_AUTOINDEX_IF_PRESENT and LIBRARY_AutoIndex then return unitsSpeed[GetUnitId(u)] else return unitSpeed[u]*0.001 endif endfunction Now that you added the PRECISION constant, shouldn't these be return (unitScale[u]/PRECISION) and return (unitSpeed[u]/PRECISION) respectively? Also, how about using a native hashtable instead of Table in order to store reals directly and avoid all those multiplications/divisions altogether? A single hashtable won't contribute much to reaching the 256-instance limit. Last edited by BBQ : 06-23-2011 at 04:22 PM. |
![]() |
![]() |
![]() |
#9 | ||
Procrastination Incarnate
Development Director
|
![]() Quote:
Quote:
|
||
![]() |
![]() |
![]() |
#10 |
Free Software Terrorist
Technical Director
|
![]() * casts Deus Ex Machina *
__________________In its current state it seems approve-friendly. I will approve it. You should consider that maybe a filter could be useful so that in certain hypothetical maps, units that keep getting changed colors periodically but do not use this do not harm performance too much. Perhaps you could use a third static condition to use a native hashtable. I would prefer it to use AutoIndex or Table instead of messing with the hashtable limit, but I guess others would disagree. |
![]() |
![]() |
![]() |
#11 | ||
Procrastination Incarnate
Development Director
|
![]() Quote:
Then, in the best case (if you're using AutoIndex), the cost of the hooked code would only be a GetUnitId call, some integer multiplications/additions and an array set, which compared to the overall cost of hooking (function call, evaluate with a bunch of arguments) and compared to the potential costs of filtering (at best an inlineable function that does a UnitTypeId check or a GetUnitUserData call plus an array read) doesn't seem like much, unless I'm completely overestimating the speed of integer maths. Quote:
Thanks for the review. |
||
![]() |
![]() |
![]() |
#12 |
Free Software Terrorist
Technical Director
|
![]() In an ideal world, someone would have updated jasshelper and make hooks take no more performance hit than a function call, but I guess you are right that in its current state an exceptions thing wouldn't matter that much.
__________________ |
![]() |
![]() |
![]() |
#13 |
User
|
![]() Useful, thank you :)
__________________ |
![]() |
![]() |
![]() |
Thread Tools | Search this Thread |
|
|