| 08-14-2008, 12:48 PM | #1 | |
Hey, I'm remaking an old spell of mine into vJass. But there's a strange bug that makes my dummies revive after I've killed them. If anyone could solve this it would be great! It uses ABC, PUI and Damage Detection as it is. Problem: When I kill the dummies in the DummBehaviour function, they somehow re-spawns at the same location and are uncontrollable. Spell tooltip:
Globals description: JASS://************************************************* //* AID_SWIFT_STRIKES: The spells' rawcode. //* UID_DUMMY_HERO: Dummy unit rawcode, with the same model as the hero. //* //* ANIMATION: Which animation is to be played? //* MULTIPLE_CAST: Are ppl allowed to refresh the spell? //* ANIMATION_SPEED: If you want to decrese/increase the dummies animation speed, this is it. //* TIME_TO_DAMAGE: The time between the animation is played and the damage is dealt. //* TRANSPARANCY: The alpha color of the dummies. //* //* DAMAGE_TYPE: Since we use Damage Detection, we have to use a custom damage_type. //* ATTACK_TYPE: Attack type of the damage. //* IGNORE_ARMOR: Wheter the damage should ignore armor or not. //* //* DAMAGE_BASE: The base % of the damage the images will deal. //* DAMAGE_MULT: The increment per level % of the damage. //* STRIKE_BASE: The base amount of images created. //* STRIKE_MULT: Increment per level of images created. //* HITS_BASE: Base amount of hits the spell will affect. //* HITS_MULT: Increment per level of hits the spell will affect. //* //* TAG_COLOR: Which color will the damage be displayed in? //* TAG_SIZE: Size of the texttag. //* TAG_VELOCITY: The y speed of the texttag. //* TAG_FADE_BEGIN: The time where the texttag begins to fade. //* TAG_LIFE_TIME: Maximum lifte-time of the texttag. //* //* BLOOD_SFX: Effect created when an image deals damage. //* DISTRACTION_SFX: The effect created when a image dies. //* ATTACH_POINT: Attachment point of blood sfx. //************************************************* The spell's main code: JASS:scope SwiftStrikes initializer InitTrig globals private constant integer AID_SWIFT_STRIKES = 'A000' private constant integer UID_DUMMY_HERO = 'h000' private constant string ANIMATION = "Attack" private constant boolean MULTIPLE_CAST = false private constant real ANIMATION_SPEED = 100.0 private constant real TIME_TO_DAMAGE = 0.5 private constant integer TRANSPARANCY = 128 private constant integer DAMAGE_TYPE = DAMAGE_TYPE_SPELL private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO private constant boolean IGNORE_ARMOR = false private constant real DAMAGE_BASE = 0.33 private constant real DAMAGE_MULT = 0.11 private constant integer STRIKE_BASE = 1 private constant integer STRIKE_MULT = 1 private constant integer HITS_BASE = 3 private constant integer HITS_MULT = 1 private constant string TAG_COLOR = "DD0000" private constant real TAG_SIZE = 0.024 private constant real TAG_VELOCITY = 0.02 private constant real TAG_FADE_BEGIN = 1.0 private constant real TAG_LIFE_TIME = 2.0 private constant string BLOOD_SFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" private constant string DISTRACTION_SFX = "Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl" private constant string ATTACH_POINT = "chest" endglobals //********************************************************* private constant function GetDamagePercent takes integer level returns real return (DAMAGE_BASE + ((level-1)*DAMAGE_MULT)) endfunction //********************************************************* private constant function GetStrikes takes integer level returns integer return (STRIKE_BASE + ((level-1)*STRIKE_MULT)) endfunction //********************************************************* private constant function GetHits takes integer level returns integer return (HITS_BASE + ((level-1)*HITS_MULT)) endfunction //********************************************************* private keyword Data // keyword used to use un-initialized stuff //********************************************************* globals private triggeraction ACTION = null private group STRIKERS = null private trigger TRIGGER = null private Data array Datas private unit array Pivot endglobals //********************************************************* private struct Data unit Caster real Damage integer Strikes integer Level integer Hits //********************************************************* static method create takes unit whichCaster returns Data local Data d local integer index = GetUnitIndex(whichCaster) local boolean b = false if (Pivot[index] == whichCaster) then // If the unit is currently using the spell.. if (MULTIPLE_CAST) then // ..check if you may cast it multiple times set d = Datas[index] // and set d to an already initialized struct set b = true // and set b to true to prevent overwriting else call DisplayTextToPlayer(GetOwningPlayer(whichCaster), 0, 0, SCOPE_PREFIX + " is already in use!") endif else set d = Data.allocate() // else create the struct and assign the values endif set d.Caster = whichCaster set d.Level = GetUnitAbilityLevel(d.Caster, AID_SWIFT_STRIKES) set d.Damage = GetDamagePercent(d.Level) set d.Strikes = GetStrikes(d.Level) set d.Hits = GetHits(d.Level) if not (b) then // If the caster is using the spell, skip this step call GroupAddUnit(STRIKERS, d.Caster) set Datas[index] = d set Pivot[index] = whichCaster endif return d endmethod //********************************************************* static method operator[] takes unit whichUnit returns Data local integer index = GetUnitIndex(whichUnit) if (Pivot[index] == whichUnit) and (IsUnitInGroup(whichUnit, STRIKERS) and Datas[index] != null) then return Datas[index]// Returns the struct if above conditions are met endif return 0 // Returns 0 if the conditions are false endmethod //********************************************************* private method onDestroy takes nothing returns nothing set Datas[GetUnitIndex(.Caster)] = 0 set Pivot[GetUnitIndex(.Caster)] = null call GroupRemoveUnit(STRIKERS, .Caster) endmethod endstruct //********************************************************* private struct Striker // Struct for dummies. Used to damage and kill the dummy unit toKill unit Damager unit Damaged real Damage //********************************************************* static method create takes unit u, unit Damager, unit Damaged, real Damage returns Striker local Striker d = Striker.allocate() set d.toKill = u // Sets the appropriate values set d.Damager = Damager set d.Damaged = Damaged set d.Damage = Damage return d endmethod private method onDestroy takes nothing returns nothing set .toKill = null set .Damager = null set .Damaged = null endmethod endstruct //********************************************************* private function ShowDamage takes unit whichUnit, real whichDamage returns nothing local texttag DTag = CreateTextTag() // Function for displaying damage. local integer correct if (StringLength(R2S(whichDamage)) < 32) then set correct = StringLength(R2S(whichDamage)) * 8 elseif (32 < StringLength(R2S(whichDamage))) then set correct = 32 * 8 endif call SetTextTagText(DTag, "|cFF" + TAG_COLOR + I2S(R2I(whichDamage)), TAG_SIZE) call SetTextTagPos(DTag, GetUnitX(whichUnit) - correct, GetUnitY(whichUnit), 16) call SetTextTagVelocity(DTag, 0, TAG_VELOCITY) call SetTextTagVisibility(DTag, true) call SetTextTagFadepoint(DTag, TAG_FADE_BEGIN) call SetTextTagLifespan(DTag, TAG_LIFE_TIME) call SetTextTagPermanent(DTag, false) set DTag = null endfunction //********************************************************* private function CreateBloodSfx takes unit whichUnit returns nothing // Function for creating special effect. call DestroyEffect(AddSpecialEffectTarget(BLOOD_SFX, whichUnit, ATTACH_POINT)) endfunction //********************************************************* private function DummyBehaviour takes nothing returns nothing // The Damage/Removal function. local timer t = GetExpiredTimer() local Striker d = GetTimerStructA(t) // Retrieves the attached struct from the timer. call UnitDamageTargetEx(d.Damager, d.Damaged, d.Damage, ATTACK_TYPE, DAMAGE_TYPE, IGNORE_ARMOR) // Deals damage to the damaged unit, (with Damage Detection function) call CreateBloodSfx(d.Damaged) // Create Blood sfx, rather obviuos. call ShowDamage(d.Damaged, d.Damage) // creates a texttag showing damage. if (GetWidgetLife(d.toKill) > 0.405) then call KillUnit(d.toKill) // <-- here we kill our dummy, but the problem is that it is revived!. call d.destroy() // Destroys struct. endif call DestroyTimer(t) set t = null endfunction //********************************************************* private function Actions takes nothing returns nothing // The damage action. local unit Damager = GetTriggerDamageSource() local unit Damaged = GetTriggerDamageTarget() local integer Type = GetTriggerDamageType() local Data d = Data[Damager] local real Damage = GetTriggerDamage() * d.Damage local real x = GetUnitX(Damager) local real y = GetUnitY(Damager) local real face = GetUnitFacing(Damager) local integer TempInt = 0 local unit striker local Striker c local timer t if (integer(d) != 0 and Type == DAMAGE_TYPE_ATTACK) then // If there's a struct and damage type is Attack then go! if (d.Hits > 0) then // Checks if the caster may still use the spell, else destroy it. set d.Hits = d.Hits - 1 loop set striker = CreateUnit(GetOwningPlayer(Damager), UID_DUMMY_HERO, x, y, face) // create 'dummy image'. exitwhen TempInt == d.Strikes // loop until all images are created. if (GetWidgetLife(striker) > 0.405) then call SetUnitTimeScale(striker, ANIMATION_SPEED * 0.01) // changes animation speed. call SetUnitAnimation(striker, ANIMATION) // playes the desired animation. call SetUnitVertexColor(striker, 255, 255, 255, TRANSPARANCY) // modifies the alpha color to the specified. endif set c = Striker.create(striker, d.Caster, Damaged, Damage) // creates a struct for the dummy, so it may damage and be killed. set t = CreateTimer() // create timer. call SetTimerStructA(t, c) // attach struct to timer. call TimerStart(t, TIME_TO_DAMAGE, false, function DummyBehaviour) // and start the Damage/Removal function. call TriggerSleepAction(0.0) // Wait lowest amount possible so not all images are created immediately. set TempInt = TempInt + 1 endloop else call d.destroy() // destroy struct if the caster has used all hits. endif endif set Damager = null // remove leaks. set Damaged = null endfunction //********************************************************* private function Conditions takes nothing returns boolean if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT) then // if a unit casts a spell.. if (GetSpellAbilityId() == AID_SWIFT_STRIKES) then // ..and that spell is swift strikes.. call Data.create(GetSpellAbilityUnit()) // ..then start the spell. endif elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH) then // else if a unit dies.. if (GetUnitTypeId(GetDyingUnit()) == UID_DUMMY_HERO) then // ..and it's a dummy.. call DestroyEffect( AddSpecialEffect(DISTRACTION_SFX, GetUnitX(GetDyingUnit()), GetUnitY(GetDyingUnit()))) // ..create an effect. endif endif return false // return false as there's no real action endfunction //********************************************************* private function InitTrig takes nothing returns nothing local trigger T = CreateTrigger() set STRIKERS = CreateGroup() // create the group that containes all casters set TRIGGER = CreateTrigger() // the trigger used for damage events set ACTION = TriggerAddAction(TRIGGER, function Actions) // the action of the above trigger call TriggerRegisterDamageEvent(TRIGGER) call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_UNIT_DEATH) call TriggerAddCondition(T, Condition( function Conditions)) endfunction endscope ---- EDIT: Changed some parts. ---- |
| 08-14-2008, 01:47 PM | #2 |
Please explain what the spell does, how you realized it, and comment your code. Then I'll be happy to spend my time helping you getting your spell to work. |
| 08-14-2008, 02:25 PM | #3 |
First post updated. |
| 08-14-2008, 10:03 PM | #4 |
Change the dummy unit's type to "Can't Raise, does not decay". |
| 08-14-2008, 10:45 PM | #5 |
Already done it, but the bug is still there. Somehow it worked fine to kill the unit manually (using my hero to attack it). |
| 08-15-2008, 12:00 AM | #6 |
Most likely the dummy is being forced to play an animation directly after it dies. Sometimes this can screw things up, and the unit will play neither the desired animation, or death, but simply "stand". I had a similar problem in a spell I made. Try adding a check for GetWidgetLife(unit) > 0.405 before setting the unit's animation. |
| 08-15-2008, 04:42 AM | #7 |
okay, now the code is readable, thanks for the comments :) something i found: JASS:
loop
set striker = CreateUnit(GetOwningPlayer(Damager), UID_DUMMY_HERO, x, y, face) // create 'dummy image'.
exitwhen TempInt >= d.Strikes // loop until all images are created.
[...] //actions with the dummy
endloop
In the case TempInt is >= d.Strikes there would still be a dummy created, but not registered for the spell. Might that be the cause? |
| 08-15-2008, 11:03 AM | #8 |
Thanks, will check those things out. EDIT: Nope it those things didn't work, but thanks anyway. EDIT_2: Updated script. |
