Sgqvur
FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
- Reaction score
- 62
FarSightBJ aka "dynamic revealing" reveals a circular area and the invisible units inside to
a player and his allies for a given duration.
Requires:
How to "install"/import this spell to your map:
This is a continuation of a thread I started roughly an year ago, which can be found here.
Back then I was thinking of using the Faerie Fire for revealing but didn't have a
method to detect when a unit was crossing a certain distance from a point.
So few days ago, basically I came across this native function called UnitShareVision(unit u, player p, bool share)
and figured that it could be used for revealing invisible units + using the 17 rect method for the
distance detection. So I tried but it turned out that the revealing part didn't quite work as I was thinking it would so
I went back to using the Faerie Fire for the revealing of invisible units.
The ScannerSweep library, it exports the scanner_sweep function:
The spell, FarSightBJ:
a player and his allies for a given duration.
Requires:
xecast library by Vexorian
ScannerSweep found here
Optional:ScannerSweep found here
GroupUtils by Rising_Dusk
TimerUtils by Vexorian
How to "install"/import this spell to your map:
1. "install"/import the xecast library.
2. go to it's configuration area and be sure to set FORCE_INVISIBLE_CAST = true
3. make an ability "extending/copying" from the Dust of Appearance ability and change it's AoE to 32.0, you need to modify the Dust of Appearance's buff if you want to change the buff's tooltip etc. for the ability. I think this is because DoA has it's buff hardcoded.
4. copy the script for ScannerSweep to a trigger
5. set the ability's id and the buff's id you made from step 3 in the configuration of ScannerSweep
6. copy the script for FarSighBJ to a trigger
7. make a point target ability and configure it's id in the FarSightBJ configuration area
0. optionally you can also "install"/import the GroupUtils and TimerUtils libraries which will save unnecessary destruction of groups and timers (will recycle groups and timers).
x. it is really that hard unfortunately =)
2. go to it's configuration area and be sure to set FORCE_INVISIBLE_CAST = true
3. make an ability "extending/copying" from the Dust of Appearance ability and change it's AoE to 32.0, you need to modify the Dust of Appearance's buff if you want to change the buff's tooltip etc. for the ability. I think this is because DoA has it's buff hardcoded.
4. copy the script for ScannerSweep to a trigger
5. set the ability's id and the buff's id you made from step 3 in the configuration of ScannerSweep
6. copy the script for FarSighBJ to a trigger
7. make a point target ability and configure it's id in the FarSightBJ configuration area
0. optionally you can also "install"/import the GroupUtils and TimerUtils libraries which will save unnecessary destruction of groups and timers (will recycle groups and timers).
x. it is really that hard unfortunately =)
This is a continuation of a thread I started roughly an year ago, which can be found here.
Back then I was thinking of using the Faerie Fire for revealing but didn't have a
method to detect when a unit was crossing a certain distance from a point.
So few days ago, basically I came across this native function called UnitShareVision(unit u, player p, bool share)
and figured that it could be used for revealing invisible units + using the 17 rect method for the
distance detection. So I tried but it turned out that the revealing part didn't quite work as I was thinking it would so
I went back to using the Faerie Fire for the revealing of invisible units.
The ScannerSweep library, it exports the scanner_sweep function:
JASS:
//! zinc
library ScannerSweep requires xecast, optional GroupUtils, optional TimerUtils
{
// Notes:
// 1. The trigger code must use TriggerAddAction not TriggerAddCondition
// because the scanner_sweep function uses TriggerSleepAction
// which doesn't work within a TriggerAddCondition.
//
// 2. The native UnitShareVision(unit u, player p, bool share) doesn't
// reveal invisible units, only gives vision of them... that is why
// the xe library by Vexorian is required. The way it works is that
// to reveal a given unit a dummy unit casts a Dust of Appearance (without other
// side effects) which does the revealing for both the owner of the dummy
// caster and his allies. The funny thing is that althogh UnitShareVision
// doesn't reveal invisible units, it reveals the area that their vision
// reveals but the IsUnitInvisible(u, p) returns false for them =)...
//
// 3. The FORCE_INVISIBLE_CAST flag in the xecast configuration must be set
// to true.
constant integer REVEALER_ID = 'A001';
constant integer REVEALER_BUFF_ID = 'Bdet';
// criteria which determine which units should be ignored
function is_unit_dummy(unit u) -> boolean { return GetUnitTypeId(u) == XE_DUMMY_UNITID; }
hashtable HT = InitHashtable();
group tmpg = CreateGroup();
xecast xc = 0;
rect R = null;
boolexpr cond_on_enter;
boolexpr cond_on_leave;
constant integer DUST_OF_APPEARANCE_ORDER_ID = 852625;
// because Blizzard forgot to make UnitShareVision take into account invisible units
// the way I would've wanted it to do =)
//
function reveal_unit_for_player_and_his_allies(unit u, player p)
{
xc.owningplayer = p;
xc.castInPoint(GetUnitX(u), GetUnitY(u));
}
// this is because TSA is way too cool to wait for the game to be paused (multiplayer)
function TSA(real dur)
{
timer t = null;
real timeRemaining = 0;
if (dur > 0)
{
static if (LIBRARY_TimerUtils) { t = NewTimer(); }
else { t = CreateTimer(); }
TimerStart(t, dur, false, null);
timeRemaining = TimerGetRemaining(t);
while (timeRemaining > 0)
{
if (timeRemaining > bj_POLLED_WAIT_SKIP_THRESHOLD) { TriggerSleepAction(0.1 * timeRemaining); }
else { TriggerSleepAction(bj_POLLED_WAIT_INTERVAL); }
timeRemaining = TimerGetRemaining(t);
}
static if (LIBRARY_TimerUtils) { ReleaseTimer(t); }
else { DestroyTimer(t); }
}
t = null;
}
struct data
{
player p;
trigger leave;
trigger enter;
region invis_detector;
group invis_units;
triggercondition on_enter_cond;
triggercondition on_leave_cond;
method stop_revealing_invisible_units()
{
TriggerRemoveCondition(enter, on_enter_cond);
DestroyTrigger(enter);
TriggerRemoveCondition(leave, on_leave_cond);
DestroyTrigger(leave);
RemoveRegion(invis_detector);
static if (LIBRARY_GroupUtils) { ReleaseGroup(invis_units); }
else { DestroyGroup(invis_units); }
destroy();
}
static method on_enter() -> boolean
{
integer i = 0;
unit u = null;
data d = 0;
u = GetTriggerUnit();
if (is_unit_dummy(u)) { u = null; return false; }
d = LoadInteger(HT, GetHandleId(GetTriggeringTrigger()), 'entr');
if (IsUnitEnemy(u, d.p) && IsUnitInvisible(u, d.p))
{
GroupAddUnit(d.invis_units, u);
reveal_unit_for_player_and_his_allies(u, d.p);
// for (0 <= i < bj_MAX_PLAYERS)
// {
// if (IsPlayerAlly(Player(i), d.p))
// {
// UnitShareVision(u, Player(i), true);
// }
// }
}
u = null;
return false;
}
static method on_leave() -> boolean
{
integer i = 0;
unit u = null;
data d = 0;
u = GetTriggerUnit();
if (is_unit_dummy(u)) { u = null; return false; }
d = LoadInteger(HT, GetHandleId(GetTriggeringTrigger()), 'leav');
if (IsUnitEnemy(u, d.p))
{
GroupRemoveUnit(d.invis_units, u);
UnitRemoveAbility(u, REVEALER_BUFF_ID);
// for (0 <= i < bj_MAX_PLAYERS)
// {
// if (IsPlayerAlly(Player(i), d.p))
// {
// BJDebugMsg("removing vision");
// UnitShareVision(u, Player(i), false);
// }
// }
}
u = null;
return false;
}
static method create(player p, real x, real y, real radius) -> data
{
real width = radius * 4.0 / 3.0;
real hw = width / 2.0;
real cx = x;
real cy = y;
real r0_minx = cx - hw;
real r0_miny = cy - hw;
real r0_maxx = cx + hw;
real r0_maxy = cy + hw;
real r1_minx = r0_minx + width * 0.15;
real r1_miny = r0_miny - width * 0.15;
real r1_maxx = r0_maxx - width * 0.15;
real r1_maxy = r0_miny;
real r2_minx = r1_minx;
real r2_miny = r0_maxy;
real r2_maxx = r1_maxx;
real r2_maxy = r0_maxy + width * 0.15;
real r3_minx = r0_minx - width * 0.15;
real r3_miny = r0_miny + width * 0.15;
real r3_maxx = r0_minx;
real r3_maxy = r0_maxy - width * 0.15;
real r4_minx = r0_maxx;
real r4_miny = r3_miny;
real r4_maxx = r4_minx + width * 0.15;
real r4_maxy = r3_maxy;
real r5_minx = r0_minx + width * 0.3;
real r5_miny = r0_miny - width * 0.225;
real r5_maxx = r0_maxx - width * 0.3;
real r5_maxy = r1_miny;
real r6_minx = r5_minx;
real r6_miny = r2_maxy;
real r6_maxx = r5_maxx;
real r6_maxy = r0_maxy + width * 0.225;
real r7_minx = r0_minx - width * 0.225;
real r7_miny = r0_miny + width * 0.3;
real r7_maxx = r3_minx;
real r7_maxy = r0_maxy - width * 0.3;
real r8_minx = r4_maxx;
real r8_miny = r7_miny;
real r8_maxx = r0_maxx + width * 0.225;
real r8_maxy = r7_maxy;
real r9_minx = r1_minx - width * 0.10;
real r9_miny = r0_miny - width * 0.10;
real r9_maxx = r1_minx;
real r9_maxy = r0_miny;
real r10_minx = r0_minx - width * 0.10;
real r10_miny = r3_miny - width * 0.10;
real r10_maxx = r0_minx;
real r10_maxy = r3_miny;
real r11_minx = r1_maxx;
real r11_miny = r9_miny;
real r11_maxx = r11_minx + width * 0.10;
real r11_maxy = r9_maxy;
real r12_minx = r0_maxx;
real r12_miny = r10_miny;
real r12_maxx = r12_minx + width * 0.10;
real r12_maxy = r10_maxy;
real r13_minx = r10_minx;
real r13_miny = r3_maxy;
real r13_maxx = r10_maxx;
real r13_maxy = r13_miny + width * 0.10;
real r14_minx = r9_minx;
real r14_miny = r0_maxy;
real r14_maxx = r9_maxx;
real r14_maxy = r14_miny + width * 0.10;
real r15_minx = r12_minx;
real r15_miny = r13_miny;
real r15_maxx = r12_maxx;
real r15_maxy = r13_maxy;
real r16_minx = r11_minx;
real r16_miny = r14_miny;
real r16_maxx = r11_maxx;
real r16_maxy = r14_maxy;
data d = data.allocate();
unit u = null;
integer i = 0;
d.invis_detector = CreateRegion();
SetRect(R, r0_minx, r0_miny, r0_maxx, r0_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r1_minx, r1_miny, r1_maxx, r1_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r2_minx, r2_miny, r2_maxx, r2_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r3_minx, r3_miny, r3_maxx, r3_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r4_minx, r4_miny, r4_maxx, r4_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r5_minx, r5_miny, r5_maxx, r5_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r6_minx, r6_miny, r6_maxx, r6_maxy);
RegionAddRect(d.invis_detector, R);
RegionAddRect(d.invis_detector, R);
SetRect(R, r7_minx, r7_miny, r7_maxx, r7_maxy);
SetRect(R, r8_minx, r8_miny, r8_maxx, r8_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r9_minx, r9_miny, r9_maxx, r9_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r10_minx, r10_miny, r10_maxx, r10_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r11_minx, r11_miny, r11_maxx, r11_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r12_minx, r12_miny, r12_maxx, r12_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r13_minx, r13_miny, r13_maxx, r13_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r14_minx, r14_miny, r14_maxx, r14_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r15_minx, r15_miny, r15_maxx, r15_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, r16_minx, r16_miny, r16_maxx, r16_maxy);
RegionAddRect(d.invis_detector, R);
SetRect(R, 0, 0, 0, 0);
d.p = p;
d.enter = CreateTrigger();
d.leave = CreateTrigger();
TriggerRegisterEnterRegion(d.enter, d.invis_detector, null);
d.on_enter_cond = TriggerAddCondition(d.enter, cond_on_enter);
TriggerRegisterLeaveRegion(d.leave, d.invis_detector, null);
d.on_leave_cond = TriggerAddCondition(d.leave, cond_on_leave);
SaveInteger(HT, GetHandleId(d.enter), 'entr', d);
SaveInteger(HT, GetHandleId(d.leave), 'leav', d);
// reveal invisible units that are present at the circular area
// immediately after the call
//
// note that there is no IsUnitInvisible check because
// if the units were revelead by other means when the call was made
// and shortyle after that it is gone, then the units will not be reveal properly
//
static if (LIBRARY_GroupUtils)
{
d.invis_units = NewGroup();
GroupUnitsInArea(tmpg, x, y, radius);
}
else
{
d.invis_units = CreateGroup();
GroupEnumUnitsInRange(tmpg, x, y, radius, null);
}
u = FirstOfGroup(tmpg);
while (u != null)
{
if (!is_unit_dummy(u))
{
if (IsUnitEnemy(u, d.p)) // && IsUnitInvisible(u, d.p)
{
reveal_unit_for_player_and_his_allies(u, d.p);
}
GroupAddUnit(d.invis_units, u);
}
// for (0 <= i < bj_MAX_PLAYERS)
// {
// if (IsPlayerAlly(Player(i), d.p))
// {
// UnitShareVision(u, Player(i), true);
// }
// }
GroupRemoveUnit(tmpg, u);
u = FirstOfGroup(tmpg);
}
u = null;
return d;
}
}
public function scanner_sweep(player p, real x, real y, real radius, real dur, string effect_location)
{
fogmodifier fms[];
unit u = null;
integer i = 0;
data d = 0 ;
effect e = null;
if (effect_location != "" || effect_location == null) { e = AddSpecialEffect(effect_location, x, y); }
// when invisible enemy units enter the circular area
// reveal them
// when they leave, stop revealing them
d = data.create(p, x, y, radius);
// gotta admit that zinc loops are awesome
for (0 <= i < bj_MAX_PLAYERS)
{
// reveal the circular area for the player and his allies
// note that each player is considered an ally to himself
if (IsPlayerAlly(Player(i), p))
{
fms<i> = CreateFogModifierRadius(Player(i), FOG_OF_WAR_VISIBLE, x, y, radius, true, false);
FogModifierStart(fms<i>);
}
}
// using a sleep because durations of < 0.1 would
// be senseless, note how the code is much "saner" this way
// without timer attaching thingies; if only sleeps were accurate =)...
// if the effect location is "Abilities\\Spells\\Other\\Andt\\Andt.mdl", i.e
// the default for the revealing abilities then the effect
// has to be destroyed ~4 seconds before the end of the duration
// to match the end of the revealing
if (effect_location == "Abilities\\Spells\\Other\\Andt\\Andt.mdl")
{
TSA(dur - 4);
DestroyEffect(e);
e = null;
TSA(4);
}
else { TriggerSleepAction(dur); }
// maybe an invisible unit entered the area but became visible before leaving
// or it never left the area
//
DisableTrigger(d.enter);
DisableTrigger(d.leave);
u = FirstOfGroup(d.invis_units);
while (u != null)
{
UnitRemoveAbility(u, REVEALER_BUFF_ID);
// for (0 <= i <= bj_MAX_PLAYERS)
// {
// if (IsPlayerAlly(Player(i), d.p))
// {
// UnitShareVision(u, Player(i), false);
// }
// }
GroupRemoveUnit(d.invis_units, u);
u = FirstOfGroup(d.invis_units);
}
d.stop_revealing_invisible_units();
// stop revealing the circular area
for(0 <= i < bj_MAX_PLAYERS)
{
if (fms<i> != null) { DestroyFogModifier(fms<i>); }
}
if (e != null) { DestroyEffect(e); }
u = null;
e = null;
}
function onInit()
{
xc = xecast.createBasic(REVEALER_ID, DUST_OF_APPEARANCE_ORDER_ID, Player(15));
R = Rect(0, 0, 0, 0);
cond_on_enter = Condition(function data.on_enter);
cond_on_leave = Condition(function data.on_leave);
}
}
//! endzinc
</i></i></i></i>
The spell, FarSightBJ:
JASS:
scope FarSightBJ initializer init // requires xebasic, ScannerSweep, optional GroupUtils, optional TimerUtils
globals
constant integer ABIL_ID = 'A000'
constant string EFFECT = "Abilities\\Spells\\Other\\Andt\\Andt.mdl"
endglobals
function AoE takes integer lvl returns real
// hm... should use the formula finder
local real result = 0
if 1 == lvl then
set result = 600
elseif 2 == lvl then
set result = 1800
else
set result = 4000
endif
return result
endfunction
function duration takes integer lvl returns real
return 0.0 * lvl + 8.0
endfunction
function FarSightBJ takes player p, real x, real y, real radius, real dur, string effect_location returns nothing
call scanner_sweep(p, x, y, radius, dur, effect_location)
endfunction
function action takes nothing returns nothing
local integer lvl = 0
if ABIL_ID != GetSpellAbilityId() then
return
endif
set lvl = GetUnitAbilityLevel(GetTriggerUnit(), ABIL_ID)
call FarSightBJ(GetOwningPlayer(GetTriggerUnit()), GetSpellTargetX(), GetSpellTargetY(), AoE(lvl), duration(lvl), EFFECT)
endfunction
function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, function action)
endfunction
endscope
Attachments
-
49.8 KB Views: 352