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!
I want to know when a player's mouse cursor collides with a hostile unit so I can use that as an event. I don't mean clicks on a unit, just when the mouse passes close enough to the model origin that the cursor color changes from grey to green/red/yellow and the temporary selection circle is shown around that unit. This is different than just polling the cursor position and checking for units within range of that X/Y because flying units exist and they aren't always located at terrain height.
Intuitively it seems that since the UI elements change when this happens it should be possible to periodically query whether those frames are currently shown. But I don't know where to start looking. Anyone? Anyone? Anyone? ...Tasyen?
I want to know when a player's mouse cursor collides with a hostile unit so I can use that as an event. I don't mean clicks on a unit, just when the mouse passes close enough to the model origin that the cursor color changes from grey to green/red/yellow and the temporary selection circle is shown around that unit. This is different than just polling the cursor position and checking for units within range of that X/Y because flying units exist and they aren't always located at terrain height.
I imagine a combination of what Uncle said and FRAMEEVENT_MOUSE_ENTER if at all, but I would think each unit must have it's own listener for this, and if that's the case I have to imagine that is deeper in the framework of unit objects than we can see...
But I think this is going to be problematic, in my experience with frames, many of them don't exist unless they are on screen, and each unit may have a listener... I hope we find an answer, and maybe even blizzards recent leaks could lead to revelations about this.
Still, Taysen is probably your only hope for confirmation, I'm merely a self-taught framer who learned almost everything from the tutorials of, you guessed it, Taysen.
You trying to mess with unit healthbars/ namplate/ labels/ thingys?
Nothing so cool, lol. I just want to make this ability amongst others for an update to my modular mouse spells pack:
My dumb ass said:
Meta Knight (passive ability) - Every N seconds the Meta Knight protecting this unit readies an extra-dimensional strike against the first hostile unit your mouse cursor touches, provided the unit is within range.
I want to float a small knight model somewhere on the UI near the portrait while the unit with the ability is selected, and then animate it to dash to and strike at a unit like Zelda's down special from SSB4 onwards. Just an oddball wordplay ability that seemed unique.
And uh... this seems like a first genuine use for SyncStoredUnit, TriggerHappy! The only other way to sync this data without a unit object is to sync the handle id of the unit and we can no longer I2H that into the actual unit. I suppose it's possible to sync both its unit type and its handle ID and then check all units of that type until I found the right one.
From what I could gather, I think you don't need frames at all.
Just use BlzGetMouseFocusUnit() in a 0.03s timer callback and sync the unit's position + type-id (3 integers, you can round the X-Y safely) when you detect a hit.
Finding the closest unit to a x-y point that has a specific type-id should be unique enough to prevent mis-targetings, unless the players are lagging severely.
In that case, I would suggest syncing more identifiers (owner player-id, unit's current order, unit's current health %, etc) and do a best-match search.
Of course, you can minimize the jank by indexing all units in your map in an array, and just syncing the array-index of the unit being hit.
Just use BlzGetMouseFocusUnit() in a 0.03s timer callback and sync the unit's position + type-id (3 integers, you can round the X-Y safely) when you detect a hit.
Finding the closest unit to a x-y point that has a specific type-id should be unique enough to prevent mis-targetings, unless the players are lagging severely.
In that case, I would suggest syncing more identifiers (owner player-id, unit's current order, unit's current health %, etc) and do a best-match search.
GetUnitFacing() might be more dependably unique than most thing other things, IF using x/y gives trouble.
A big part of me thinks x/y alone should cut it - I can hardly get 4 units through a door without them getting in each other's way and turning it into a huge ordeal... sometimes fast is slow in wc3 if it's a moving crowd, but who knows how it might be used.
The index idea on the other hand is bullet proof and would maybe be the only thing to solve my irrational paranoia... but maybe I really am worrying too much... still who knows how it might be used by others, and this guarantees that the one player who actually hovered the unit gets their target.
I haven't thought of a realistic scenario where units could be close enough...
Edit (immediately after posting): A bunch of moving fliers just came to mind as a hazard mis-hover if an index or something similar isn't used.
Of course, coordinate search! Thank you. Seems like handle ID + XY is foolproof. I presume, though, that the unit sync should work... right? We just have no idea how fast a unit sync can occur because nobody ever needed one before? I don't want to involve an indexer unless I have to because I haven't needed one yet for these spells.
Okay here's what's stumping me now, trying to use Sync:
JASS:
static method periodic takes nothing returns nothing
local SpellData sd
local unit mU = null
set CURR = 0
loop
exitwhen CURR >= COUNT
set sd = GSD[CURR]
if not UnitAlive(sd.c) and not IsUnitType(sd.c, UNIT_TYPE_HERO) then
call sd.endInstance()
endif
if IsUnitSelected(sd.c, sd.p) and BlzGetUnitAbilityCooldownRemaining(sd.c, SPELL_ID) == 0 then
if GetLocalPlayer() == sd.p then
set mU = BlzGetMouseFocusUnit()
if mU != null and IsUnitEnemy(mU, sd.p) and UnitAlive(mU) and /*
*/ not IsUnitType(mU, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not BlzIsUnitInvulnerable(mU) and /*
*/ not IsUnitInGroup(mU, sd.HIT_GROUP) then
//===========
//PROBLEM AREA
//===========
//call ExplodeUnitBJ(mU)
call BlzStartUnitAbilityCooldown(sd.c, SPELL_ID, 2.) //probably should start CD here rather than later???
endif
endif
endif
set mU = null
// call sd.onFound()
set CURR = CURR+1
endloop
endmethod
Inside the LocalPlayer block I can query the properties of mU to see if it's an acceptable target even before the data is synced. Filtering out nearly-false-positives at this juncture seems ideal, but don't see a way to actually... communicate the difference between a good/bad mU outside of that local block. I don't need mU directly (only knowledge about it) but I'd ideally not like to sync 3 integers per spell instance every 0.0325 if I don't have to.
Intuitively I feel I've 'discovered' the reason we have to muck around with this sync bologna at all: if I could communicate a boolean outside of this block I could communicate arbitrary data by bit with boolean arrays. Am I correct in this assessment? If so there's no other way I can approach this than to sync the data for every non-cooling-down instance on every timer period even if there wasn't a unit under the mouse, right? That would look like this:
JASS:
static method onSync takes nothing returns nothing
local SyncData sdata = GetSyncedData()
local SpellData sd = SpellData(sdata.readInt(0)) //0 first?
local int hid = sdata.readInt(1)
local real ux = sdata.readInt(2)
local real uy = sdata.readInt(3)
//do some stuff
call sdata.destroy() //yes?
endmethod
static method periodic takes nothing returns nothing
local SpellData sd
local unit mU = null
local SyncData sdata
set CURR = 0
loop
exitwhen CURR >= COUNT
set sd = GSD[CURR]
if not UnitAlive(sd.c) and not IsUnitType(sd.c, UNIT_TYPE_HERO) then
call sd.endInstance()
endif
if IsUnitSelected(sd.c, sd.p) and BlzGetUnitAbilityCooldownRemaining(sd.c, SPELL_ID) == 0 then
if GetLocalPlayer() == sd.p then
set mU = BlzGetMouseFocusUnit()
endif
set sdata = SyncData.create()
call sdata.addInt(sd)
call sdata.addInt(GetHandleId(mU))
call sdata.addInt(R2I(GetUnitX(mU))
call sdata.addInt(R2I(GetUnitY(mU))
call sdata.addEventListener(function thistype.onSync)
call sdata.start() //Start after or before adding?
endif
set mU = null
set CURR = CURR+1
endloop
endmethod
EDIT
For some reason I didnt see that you had already posted your code.
I think I understand what you are saying now!
A major problem with that code that you correctly identified is that you cant create a listener (.addEventListener) in a local context, so the whole thing is just syncing every loop of the timer, because how could it not?
The way to solve this is to use what I just said below:
ORIGINAL REPLY
I don't need mU directly (only knowledge about it) but I'd ideally not like to sync 3 integers per spell instance every 0.0325 if I don't have to.
I dont think you need to sync anything every 0.0325s.
First, create the sync trigger when the map starts (Time Elapsed 0.01s),using BlzTriggerRegisterPlayerSyncEvent.
Once the spell is cast (or is off cooldown), I would start a timer that runs every 0.025s.
That timer will check if BlzGetMouseFocusUnit() is a valid target for the spell.
If true (the currently hovered unit is a valid target), then I would use BlzSendSyncData() to send:
JASS:
unit u = BlzGetMouseFocusUnit()
call BlzSendSyncData("MouseHoverSpell", I2S(R2I(GetUnitX(u))) + "," + I2S(R2I(GetUnitY(u))) + "," + I2S(GetUnitTypeId(u)))
On the sync trigger's actions, you parse the received string (BlzGetTriggerSyncData()) back into the x,y,typeid of the unit, then search for the target unit on the map using one of the GroupEnumUnits functions. After the unit is found and validated, you can then start the cooldown of the spell, deal the damage, whatever else. However...
I think the safe standard for "assuming laggy conditions" is a 1s delay, and in that case I can see some mix-ups possibly happening if a group of identical units (12 recently trained footmen) is on the move and one of them gets slashed. Perhaps the delay in sending+receiving the synced string could have one footman take over the position of the "real target". A indexing system would be the best way to prevent this from happening.
If an indexer is too much overhead, you can always use SetUnitUserData() to set a unique integer to each unit as they enter the Playable Map Area, and sync just that integer. Some maps might already be using that for something though (which leads you back to spinning up your own indexer )
Thanks for the detailed reply. What I have learned from you is that Sync is mostly deprecated now if you can convert your async data to a string and then get by on that using BlzSendSyncData with a proper prefix. So a couple follow-up questions and comments for anyone to answer:
What does the fromServer boolean in BlzTriggerRegisterPlayerSyncEvent control?
What does the boolean returned by BlzSendSyncData indicate?
Does using BlzSendSyncData hang the game like syncing through selections and gamecache does? That was my main concern with trying to constantly sync a bunch of information. Maybe I misunderstand something about the nuances here.
Instead of X+Y+uType and then doing a GroupEnum in the right area which could possibly fail in contrived scenarios, doesn't using uType+HandleID and then doing a UnitsOfType to find the right handle completely avoid the issue? I floated that earlier in this thread and either nobody caught it or you've stuck to uType+X+Y for some intentional reason and I'm curious about that.
And a comment: an indexer is not too much overhead, lol. I just didn't need it for the other spells in the pack and would ideally avoid it if I can. While I agree that every map would be better off having been built with an indexer in mind/fully integrated (to benefit all of its code), it feels to me a little bit like bundled bloatware that affects something deep down in your map operation for a small benefit on the surface.
an indexer is not too much overhead, lol. I just didn't need it for the other spells in the pack and would ideally avoid it if I can. While I agree that every map would be better off having been built with an indexer in mind/fully integrated (to benefit all of its code), it feels to me a little bit like bundled bloatware that affects something deep down in your map operation for a small benefit on the surface.
I like to make indexers with a limited scope for situations that could benefit from one but may not want to index everything like an indexer usually would.
For example, in my personal knock-back system, I used an indexer that only holds units while they are being tossed around, then ditches them afterward.
Reason being I don't want to enumerate the whole index when there are only a few units that it needs to track.
On a plane, if this comment gains any interest I'll update late.
@moddiemads thanks for sharing all of that, I've learned plenty already from this thread about syncing.
I never found out, but everyone in the community that uses it just assumes that it means: true if the data will be synced, false if it won't (so you need to try syncing again in the next tick).
Does using BlzSendSyncData hang the game like syncing through selections and gamecache does? That was my main concern with trying to constantly sync a bunch of information. Maybe I misunderstand something about the nuances here.
Hmmm, I'm not sure about this. Maybe it does, but I'd say it depends on how busy your map is. If syncing data is getting in the way of the gameplay, then instead of converting the ints to strings directly and creating a huge-ass string, you could use FourCC encoding (where every-ish integer is just 4 chars (exceptions would require a couple extra characters)).
Instead of X+Y+uType and then doing a GroupEnum in the right area which could possibly fail in contrived scenarios, doesn't using uType+HandleID and then doing a UnitsOfType to find the right handle completely avoid the issue? I floated that earlier in this thread and either nobody caught it or you've stuck to uType+X+Y for some intentional reason and I'm curious about that.
If I recall correctly, handle IDs are no longer synced. Clients can have different handle Ids for the same object. The current best practice is to never use GetHandleId for anything anymore.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.