- Joined
- Mar 20, 2014
- Messages
- 67
Hi all,
Bit of a weird problem
I have a spell that creates an area of toxic gas -- Inside that gas the unit becomes invisible, only showing while attacking or casting a spell (permanent invisibility), if they leave the area the permanent invisibility gets removed (works 100%, even if he recasts then leaves again)
However I have this weird bug that happens when the spell ends and the unit is still inside the AOE
He stays permanently invis
My code is in Zinc, however you can read it like it's vJASS
Things I've tried
- 3 different permanent invis abilities
- Adding it to a spellbook, disabling the spellbook, and adding/removing the spellbook instead
- Moving the unit group selection area somewhere else so for the last second it'll select nothing and it might remove the ability normally
- Debugging, I've removed it in every possible place when it ends and nothing will work
Any help is appreciated
Bit of a weird problem
I have a spell that creates an area of toxic gas -- Inside that gas the unit becomes invisible, only showing while attacking or casting a spell (permanent invisibility), if they leave the area the permanent invisibility gets removed (works 100%, even if he recasts then leaves again)
However I have this weird bug that happens when the spell ends and the unit is still inside the AOE
He stays permanently invis
My code is in Zinc, however you can read it like it's vJASS
Things I've tried
- 3 different permanent invis abilities
- Adding it to a spellbook, disabling the spellbook, and adding/removing the spellbook instead
- Moving the unit group selection area somewhere else so for the last second it'll select nothing and it might remove the ability normally
- Debugging, I've removed it in every possible place when it ends and nothing will work
Any help is appreciated
Code:
//! zinc
library AOEInvis requires GT, GameTimer, xebasic, xepreload, xefx, IsUnitWard, xecast {
private struct AOEInvis{
integer abilityId = 'TSAW';
integer invisAbilityId = 'A090';
integer dummySlowAbilityId = 'A08S';
integer dummyUnitId = 'e01B';
real damagePerSecond;
real timerSpeed = .25;
integer poisonBuffId = 'B038';
integer poisonRadius;
unit caster;
unit triggerUnit;
real casterX;
real casterY;
real duration;
boolean dummyCreated;
damagetype damageType;
static string groundEffect;
static string builderEffect;
//static string titanEffect;
xefx poisonCloudDummy;
xefx poisonCloudDummy2;
xefx poisonDamageDummy;
unit dummyUnit;
GameTimer periodicTimer;
GameTimer durationTimer;
effect groundSpecialEffect;
private method Setup(integer level) {
//How much damage per second should it do?
this.damagePerSecond = (4+ (5))/(1/this.timerSpeed);
//The radius of the effect
this.poisonRadius = 700;
//The damage type of the ability
this.damageType = DAMAGE_TYPE_MAGIC;
//The duration of the ability
this.duration = 46;
//Poison cloud effect
this.groundEffect = "war3mapImported\\GreenCloudOfFog.mdx";
//Poison damage/slow effect
this.builderEffect = "war3mapImported\\DebuffPoisoned.mdx";
//this.titanEffect = "war3mapImported\\DebuffPoisoned.mdx";
}
private method DamageUnit(unit u) {
effect e = AddSpecialEffectTarget(this.builderEffect, u, "origin");
UnitDamageTarget(this.caster, u, this.damagePerSecond, false, false, ATTACK_TYPE_CHAOS, this.damageType, null);
DestroyEffect(e);
}
private method CheckTarget(unit u) -> boolean {
player p = GetOwningPlayer(this.caster); // Apparently player handles do not leak, so this is good!
return (IsUnitEnemy(u, p) ||
GetOwningPlayer(u) == Player(PLAYER_NEUTRAL_PASSIVE)) && // Alliances
!IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) && // Magic
!IsUnitType(u, UNIT_TYPE_STRUCTURE) && // Organic only
!IsUnitType(u, UNIT_TYPE_MECHANICAL) &&
!IsUnitWard(u) && // No wards
UnitAlive(u); // Is Alive
}
public method tick() {
group g = CreateGroup();
//Group T is to select the units in range to determine if they are outside group G but
//still have the buffs
//If they're not then we remove the permanent invisibility/the damage debuff
group t = CreateGroup();
unit u = null;
effect e;
GroupEnumUnitsInRange(g, this.casterX, this.casterY, this.poisonRadius, null);
GroupEnumUnitsInRange(t, this.casterX, this.casterY, this.poisonRadius + 200, null);
u = FirstOfGroup(t);
//If the first unit is equal to null there's no units
while(u != null) {
if(UnitManager.isTitan(u) || UnitManager.isMinion(u)) {
//Titan or Mini in group
//Add invis to them
//Check if unit is in the main cloud and in an area outside of it
//If the unit is inside the main cloud we add the invis ability if not already added
//If the unit is outside the main cloud then we remove the invis ability
if(IsUnitInGroup(u, t) && IsUnitInGroup(u, g)) {
if(GetUnitAbilityLevel(u, this.invisAbilityId) < 1) {
UnitAddAbility(u, this.invisAbilityId);
}
} else if(IsUnitInGroup(u, t) && !IsUnitInGroup(u, g)) {
UnitRemoveAbility(u, this.invisAbilityId);
}
} else if(this.CheckTarget(u) && IsUnitInGroup(u, g)) {
if(!this.dummyCreated) {
this.dummyUnit = CreateUnit(GetOwningPlayer(this.caster), this.dummyUnitId, this.casterX, this.casterY, bj_UNIT_FACING);
UnitAddAbility(this.dummyUnit, this.dummySlowAbilityId);
SetUnitAbilityLevel(this.dummyUnit, this.dummySlowAbilityId, 1);
UnitApplyTimedLife(this.dummyUnit, 'BTLF', this.duration);
this.dummyCreated = true;
this.DamageUnit(u);
} else {
this.DamageUnit(u);
}
} else if(this.CheckTarget(u) && !IsUnitInGroup(u, g)) {
UnitRemoveAbility(u, this.poisonBuffId);
}
GroupRemoveUnit(t, u);
u=null;
u = FirstOfGroup(t);
}
DestroyGroup(g);
DestroyGroup(t);
u=null;
g=null;
t=null;
}
private static method Begin(unit caster) -> thistype {
thistype this = thistype.allocate();
this.caster = caster;
this.Setup(GetUnitLevel(this.caster));
this.casterX = GetUnitX(this.caster);
this.casterY = GetUnitY(this.caster);
//Start timer here to check for units
SetPlayerAbilityAvailable(GetOwningPlayer(this.caster), this.invisAbilityId, false);
this.poisonCloudDummy = xefx.create(this.casterX, this.casterY, GetUnitFacing(this.caster) * bj_DEGTORAD);
this.poisonCloudDummy.fxpath = this.groundEffect;
this.poisonCloudDummy.x = this.casterX;
this.poisonCloudDummy.y = this.casterY;
this.poisonCloudDummy.z = 10;
this.poisonCloudDummy.scale = 1.20;
this.poisonCloudDummy2 = xefx.create(this.casterX, this.casterY, GetUnitFacing(this.caster) * bj_DEGTORAD);
this.poisonCloudDummy2.fxpath = this.groundEffect;
this.poisonCloudDummy2.x = this.casterX;
this.poisonCloudDummy2.y = this.casterY;
this.poisonCloudDummy2.z = 10;
this.poisonCloudDummy2.scale = 1.20;
//Start our two timers (one to check collision the other to end the current poison cloud
this.periodicTimer = GameTimer.newPeriodic(function(GameTimer t){
thistype this = t.data();
this.tick();
}).start(this.timerSpeed);
this.periodicTimer.setData(this);
this.durationTimer = GameTimer.new(function(GameTimer t) {
thistype this = t.data();
//Temporary group and unit meant to clear all units of the poison debuff should they have been affected
group g=null;
unit u=null;
this.periodicTimer.deleteLater();
//On end of duration we terminate all buffs that are active
g=CreateGroup();
GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null);
u=FirstOfGroup(g);
while(u!=null) {
//If any defenders have the buff we remove it
if(GetUnitAbilityLevel(u, this.poisonBuffId) > 0) {
UnitRemoveAbility(u, this.poisonBuffId);
}
//If any titans or minis accidentally still have the invis ability we remove it
if(UnitManager.isMinion(u) || UnitManager.isTitan(u)) {
UnitRemoveAbility(u, this.invisAbilityId);
}
GroupRemoveUnit(g, u);
u=null;
u=FirstOfGroup(g);
}
DestroyGroup(g);
u=null;
g=null;
this.dummyUnit = null;
this.durationTimer.deleteLater();
this.poisonCloudDummy.destroy();
this.poisonCloudDummy2.destroy();
}).start(this.duration);
this.durationTimer.setData(this);
return this;
}
private static method onCast() {
unit caster = GetSpellAbilityUnit();
thistype.Begin(caster);
}
private static method onAbilitySetup(){
trigger t = CreateTrigger();
thistype this = thistype.allocate();
integer id = this.abilityId;
this.destroy();
GT_RegisterStartsEffectEvent(t, id);
TriggerAddCondition(t, Condition(function() -> boolean {
thistype.onCast();
return false;
}));
XE_PreloadAbility(id);
}
private static method onInit() {
thistype.onAbilitySetup();
}
}
}
//! endzinc