| 03-29-2009, 07:18 AM | #1 |
Never mind the title. I figured it out shortly after posting this, but can't delete the thread. I'm trying to help another user (deolrin) with a "fear" spell that causes a unit to run away from the position of the caster. I'm using a struct to contain a unit and various "things" that need to remain associated to that particular unit. I'm also using timers because (1) I need to learn how to use timers and (2) deolrin wants the spell's effect to stop on either of two conditions: time expires or the unit is damaged. Originally, this was a problem I was asking about, but I figured it out myself (forgot to destroy a trigger I was done with, though I still don't understand why it caused the specific problem it did). Unfortunately, it seems I can't delete this post, so instead I'm opening it up to criticism. Oh, and the only library I'm using is Vexorian's TimerUtils (blue flavor). Update: fixed some singularly nasty bugs. Fear:scope Fear // Calibration globals constant integer FEAR_ABILITY = 'A000' // The spell ID for the Fear ability constant real FEAR_STEP = 250 // How far the unit should travel at each interval constant real FEAR_PERIOD = 0.75 // How often you want the unit to change direction constant real FLEE_ANGLE_DENOM = 8.00 // The angle to add or subtract from the unit's range of motion. // A value of 8 will make the unit give or take PI/8 rad = 45 degrees. // A value of 16 will make the unit give or take PI/16 rad = 22.5 degrees. constant player FEAR_GIVE_CONTROL = Player(PLAYER_NEUTRAL_PASSIVE) // Which player should take control of the Afraid unit // (you may wish to set this to Neutral Hostile, if you want it to be // auto-attackable) endglobals function Duration takes integer lvl returns real return lvl * 4.00 endfunction // End Calibration globals private Afraid array Scared private trigger array FearCancel private integer top = 0 private integer bottom = 0 private timer keepRunning endglobals struct Afraid private unit fleeing private real fleeAngle private player original private integer data static method create takes unit f, real fT returns Afraid // Parameter names are arbitrary and confusing mostly just because I ran out of ideas. // But basically this bit just makes a new Afraid type with a unit, angle to run towards, // and the handle ID of the timer that will stop it. local Afraid A = Afraid.allocate() set A.fleeing = f set A.fleeAngle = fT set A.original = GetOwningPlayer(f) set A.data = GetUnitUserData(f) // This spell cannot target Neutral Passive units, so multiple instances of this on the same // unit will not be a problem. The custom data is used to store the appropriate index // for this struct instance and its corresponding trigger, so that the unit itself // can refer to its own struct from the Unit Takes Damage event. call SetUnitUserData(f, top) call SetUnitOwner(A.fleeing, FEAR_GIVE_CONTROL, false) return A endmethod method Stop takes nothing returns nothing // Essentially, set all the stuff to null. call SetUnitOwner(.fleeing, .original, false) call IssueImmediateOrder(.fleeing, "stop") call DestroyTrigger(FearCancel[GetUnitUserData(.fleeing)]) set FearCancel[GetUnitUserData(.fleeing)] = null call SetUnitUserData(.fleeing, .data) set .fleeing = null set .original = null call .destroy() endmethod method Run takes nothing returns nothing // Forces the unit to move to a point in a specific general direction, offset by a random angle // specified in radians during calibration. if .fleeing != null then call IssuePointOrder(.fleeing, "move", GetUnitX(.fleeing)+FEAR_STEP*Cos(.fleeAngle+GetRandomReal((0-bj_PI)/FLEE_ANGLE_DENOM, bj_PI/FLEE_ANGLE_DENOM)), GetUnitY(.fleeing)+FEAR_STEP*Sin(.fleeAngle+GetRandomReal((0-bj_PI)/FLEE_ANGLE_DENOM, bj_PI/FLEE_ANGLE_DENOM))) endif endmethod endstruct function Cancel takes nothing returns boolean call Scared[GetUnitUserData(GetTriggerUnit())].Stop() return true endfunction function Timeout takes nothing returns nothing call Scared[GetTimerData(GetExpiredTimer())].Stop() call ReleaseTimer(GetExpiredTimer()) set bottom = bottom + 1 if bottom == JASS_MAX_ARRAY_SIZE then set bottom = 0 endif endfunction function RunAll takes nothing returns nothing local integer i = bottom if bottom <= top then loop exitwhen i == top call Scared[i].Run() set i = i + 1 endloop else loop exitwhen i == JASS_MAX_ARRAY_SIZE call Scared[i].Run() set i = i + 1 endloop set i = 0 loop exitwhen i == top call Scared[i].Run() set i = i + 1 endloop endif endfunction function Fear_Condition takes nothing returns boolean return GetSpellAbilityId() == FEAR_ABILITY and (GetOwningPlayer(GetSpellTargetUnit()) != FEAR_GIVE_CONTROL) endfunction function Fear_Start takes nothing returns boolean local unit fleeing = GetSpellTargetUnit() local real fleeAngle = Atan2(GetUnitY(fleeing)-GetUnitY(GetSpellAbilityUnit()), GetUnitX(fleeing)-GetUnitX(GetSpellAbilityUnit())) local timer timeout = NewTimer() local integer lvl = GetUnitAbilityLevel(GetSpellAbilityUnit(), FEAR_ABILITY) call SetTimerData(timeout, top) call TimerStart(timeout, Duration(lvl), false, function Timeout) set Scared[top] = Afraid.create(fleeing, fleeAngle) set FearCancel[top] = CreateTrigger() call Scared[top].Run() set top = top + 1 if top == JASS_MAX_ARRAY_SIZE then set top = 0 endif call PolledWait(1) call TriggerRegisterUnitEvent(FearCancel[top-1], fleeing, EVENT_UNIT_DAMAGED) call TriggerAddCondition(FearCancel[top-1], Condition(function Cancel)) set fleeing = null return true return false endfunction function InitTrig_Fear takes nothing returns nothing local integer index set keepRunning = NewTimer() call TimerStart(keepRunning, FEAR_PERIOD, true, function RunAll) set gg_trg_Fear = CreateTrigger() set index = 0 loop call TriggerRegisterPlayerUnitEvent(gg_trg_Fear, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null) set index = index + 1 exitwhen index == bj_MAX_PLAYER_SLOTS endloop call TriggerAddCondition(gg_trg_Fear, Condition(function Fear_Condition)) call TriggerAddAction(gg_trg_Fear, function Fear_Start) endfunction endscope |
| 03-29-2009, 02:16 PM | #2 |
Ugh, dynamic triggers and direct custom value use... that's ugly. |
| 03-29-2009, 11:39 PM | #3 |
What about "inner beauty"? :P Anyway, yeah, Awesome Ownagealacaster already explained why dynamic triggers are considered "ugly," and actually showed me one of your spells to illustrate the potential of Handle Tables. So even though I still haven't really figured out timers (y'all keep insisting that you can pass locals to timers, but I just don't see a way...), this new Table thang is going on the list of things to learn. Unfortunately (well, depending on how you look at it), I seem to be making too much of a priority out of all this cool vJASS stuff (which is probably getting boring to you by now, since you went and mastered it while I was wasting time on superficial Naga icons and Diablo II items that vanished last year after I stopped playing). I really ought to be doing my homework. If I can just peel myself away from this here computer screen... nnnnn... |
