wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Warcraft III Modding > Developer's Corner > Triggers & Scripts
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 03-05-2013, 03:26 PM   #1
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default UHS - Item Request #1

Undead Hero Siege needs items that will benefit teamplay.

We need an item with a complicated ability.

You can choose the name and the icon for the item.
Item ability must be MPI (multi player instanceable)

Effect:
When item is equipped on a hero it will automatically cast Spirit Touch (mass mana regen) every X seconds.
X has a random shift of 1-3 seconds to avoid all heroes always casting the spell at the same time.
The overhead spell effect on hero must be properly displayed on every cast.
Item autocast needs Y mana and will not work if hero does not have mana.
Hero can turn on/off item autocasting by clicking on the item, this should change the item icon.
Turning on/off costs Y mana.
__________________
<nosignature />

Last edited by cohadar : 03-05-2013 at 03:27 PM.
cohadar is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 03-06-2013, 02:14 AM   #2
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

Not sure if I understood the request correctly but I gave it a try.
I think it would look great with the icons for Mana Flare on and off (active & inactive version).
I can't think of an appropriate name and description, that's up to someone more creative.

Also, in order for this to work the Inactive version must be undroppable!
Actually, players must not be allowed to manually pick an Inactive version (like from the ground or from a shop). Otherwise this bugs.
Wasn't sure if Spirit Touch should restore mana to the owner so I left it as such.
Collapse Zinc:
//! zinc

// Should exclude caster on Spirit Touch restoration?
// NOTE: Inactive version must be undroppable item!

library ItemRequest01 requires TimedLoop
{
    // Raw code of the ability supplied in items.
    // Both active & inactive version will share it.
    constant integer AID_ITEM_SWITCH = 'A000';
    
    // Self-descriptive (active & inactive versions):
    constant integer IID_ACTIVE_ITEM = 'I000';
    constant integer IID_INACTIVE_ITEM = 'I001';
    
    // When passive Spirit Touch procs, this effect is applied on the caster:
    constant string SFX_ON_CASTER = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl";
    constant string AP_ON_CASTER = "overhead";
    // Units that get mana restored get this effect as well:
    constant string SFX_ON_TARGET = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl";
    constant string AP_ON_TARGET = "chest";
    
    // Self-descriptive functions:
    
    function GetAoE(unit whichUnit) -> real
    {
        return 475.;
    }
    
    // NOTE: Mana is taken when the passive spirit touch procs.
    function GetManaCost(unit whichUnit) -> real
    {
        return 25.;
    }
    
    function GetManaRestored(unit whichUnit) -> real
    {
        return 50.;
    }
    
    // Hopefully this inlines (if I understand inline rules correctly).
    // These are used to determine interval between mana restoration.
    // Used as in GetRandomReal(min_bound, max_bound).
    function GetMinRandBound(unit whichUnit) -> real
    {
        return 1.;
    }
    function GetMaxRandBound(unit whichUnit) -> real
    {
        return 3.;
    }
//===========================================================================
//       END OF CONFIGURATION
//===========================================================================

    trigger TRIGGER_ON_PICK = CreateTrigger();
    group GROUP = CreateGroup();
    unit UNIT = null;

        
    struct Data
    {
        unit cast;
        timer tim;
        integer ticks;
        
        static method SetAlliesMana() // need better name
        {
            unit enum = GetEnumUnit();
            
            SetUnitState(enum, UNIT_STATE_MANA, /*
            */ GetUnitState(enum, UNIT_STATE_MANA) + GetManaRestored(UNIT));
            DestroyEffect(AddSpecialEffectTarget(SFX_ON_TARGET, enum, AP_ON_TARGET));
            
            enum = null;
        }
        
        static method GetAllies() -> boolean
        {
            return ( IsUnitAlly(GetFilterUnit(), GetOwningPlayer(UNIT)) && /*
            */  GetUnitState(GetFilterUnit(), UNIT_STATE_MANA) > 0. );
        }
        
        method applyRestoration()
        {
            real mana = GetUnitState(this.cast, UNIT_STATE_MANA);
            real mana_req = GetManaCost(this.cast);
            
            if ( mana >= mana_req )
            {
                UNIT = this.cast;
                SetUnitState(UNIT, UNIT_STATE_MANA, mana - mana_req);
                DestroyEffect(AddSpecialEffectTarget(SFX_ON_CASTER, UNIT, AP_ON_CASTER));
                
                GroupEnumUnitsInRange(GROUP, GetUnitX(UNIT), GetUnitY(UNIT), /*
                */ GetAoE(UNIT), Condition(function thistype.GetAllies));
                ForGroup(GROUP, function thistype.SetAlliesMana); // need better name
            }
        }
        
        method onTimedLoop() -> boolean
        {
            boolean hasActive = UnitHasItemOfTypeBJ(this.cast, IID_ACTIVE_ITEM);
            
            if ( UnitHasItemOfTypeBJ(this.cast, IID_ACTIVE_ITEM) )
            {
                this.ticks -= 1;
                
                if ( this.ticks <= 0 )
                {
                    this.applyRestoration();
                    this.ticks = R2I(GetRandomReal(GetMinRandBound(this.cast), GetMaxRandBound(this.cast)));
                    this.ticks *= R2I(1. / TimedLoop_PERIOD);
                }
            }
            else if ( UnitHasItemOfTypeBJ(this.cast, IID_INACTIVE_ITEM) )
            {
                this.ticks -= 1;
                // Ticks should get reduced even when inactive version is on the unit.
                // This way when somebody switches to active, the restoration will be ready.
            }
            else
                return TimedLoop_STOP;

            return TimedLoop_CONTINUE;
        }
        
        module TimedLoop;
        
        static method create(unit whichUnit) -> thistype
        {
            thistype this = thistype.allocate();
            
            this.cast = whichUnit;
            this.ticks = R2I(GetRandomReal(GetMinRandBound(this.cast), GetMaxRandBound(this.cast)));
            this.ticks *= R2I(1. / TimedLoop_PERIOD);
            this.applyRestoration();
            
            this.startTimedLoop();
            
            return this;
        }
    }
    
    function OnItemPickup() -> boolean
    {
        unit cast = GetTriggerUnit();
        item it = GetManipulatedItem();
        integer itemId = GetItemTypeId(it);
     
        if ( itemId == IID_ACTIVE_ITEM || itemId == IID_INACTIVE_ITEM )
        {
            UnitRemoveItem(cast, it);
            DisableTrigger(TRIGGER_ON_PICK);
            if ( UnitHasItemOfTypeBJ(cast, IID_ACTIVE_ITEM) || /*
            */   UnitHasItemOfTypeBJ(cast, IID_INACTIVE_ITEM) )
            {
            }
            else
            {
                UnitAddItem(cast, it);
                if ( itemId == IID_ACTIVE_ITEM )
                    Data.create(cast);
            }
            EnableTrigger(TRIGGER_ON_PICK);
            
        }
        
        cast = null; it = null;
        return false;
    }
    
    function Actions()
    {
        unit cast = GetTriggerUnit();
        
        if ( UnitHasItemOfTypeBJ(cast, IID_ACTIVE_ITEM) )
        {
            RemoveItem(GetItemOfTypeFromUnitBJ(cast, IID_ACTIVE_ITEM));
            bj_lastCreatedItem = CreateItem(IID_INACTIVE_ITEM, GetUnitX(cast), GetUnitY(cast));
        }
        else 
        {
            RemoveItem(GetItemOfTypeFromUnitBJ(cast, IID_INACTIVE_ITEM));
            bj_lastCreatedItem = CreateItem(IID_ACTIVE_ITEM, GetUnitX(cast), GetUnitY(cast));
        }
        DisableTrigger(TRIGGER_ON_PICK);
        UnitAddItem(cast, bj_lastCreatedItem);
        EnableTrigger(TRIGGER_ON_PICK);
        
        cast = null;
    }
    
    function Conditions() -> boolean
    {
        return ( GetSpellAbilityId() == AID_ITEM_SWITCH );
    }
    
    function onInit()
    {
        trigger trig = CreateTrigger();
        
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_ENDCAST);
        TriggerAddCondition(trig, Condition(function Conditions));
        TriggerAddAction(trig, function Actions);
        
        TriggerRegisterAnyUnitEventBJ(TRIGGER_ON_PICK, EVENT_PLAYER_UNIT_PICKUP_ITEM);
        TriggerAddCondition(TRIGGER_ON_PICK, Condition(function OnItemPickup));
    }
}

//! endzinc

Last edited by HerrStanev : 03-10-2013 at 10:51 PM.
HerrStanev is offline   Reply With Quote
Old 03-06-2013, 06:26 AM   #3
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

The period is X + (1 to 3) seconds, not (1 to 3)
So if X is 10, the item will regen mana at: 10+1, 20-2, 30+3, 40-1, 50+2,...


Quote:
Originally Posted by HerrStanev
Also, in order for this to work the Inactive version must be undroppable!
Actually, players must not be allowed to manually pick an Inactive version (like from the ground or from a shop). Otherwise this bugs.
This is simply not acceptable. There is no reason why players should not be able to pick and drop both active and inactive version of item.
Also there is no reason why players should not be able to pick more than one of this items at the same time, and even have both active and inactive versions in inventory at the same time.

Would you like me to describe the algorithm to you or you prefer to take the challenge?
TIP: note that I was talking about players picking items, not heroes.
__________________
<nosignature />

Last edited by cohadar : 03-06-2013 at 06:27 AM.
cohadar is offline   Reply With Quote
Old 03-06-2013, 04:05 PM   #4
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

I knew I've misunderstood what you wanted, especially the period.
I assumed only 1 item was allowed per unit.
Reworked this but I'm really not sure what the mechanics should be.
What I've accomplished is this:
- unit picks up active version - Spirit Touch struct activates
- after <insert formula w/ random period> time, Spirit Touch procs
- picked active item gets checked periodically if it's still on unit
- if not on unit, abort timed Spirit Touch (possibly the item is dropped or the inactive version is obtained)

Btw, to prevent abuses with actively picking and dropping the active item, on first picking of the item the first Spirit Touch will proc after X +- 1-3 seconds, not on struct creation. This impacts shifting from Inactive to Active because this way you have to wait X +- 1-3 secs before Spirit Touch procs. Workaround might be to apply restoration whenever shift from Inactive to Active is detected?

Collapse Zinc:
//! zinc

library IReq01 requires TimedLoop
{

    constant integer IID_ACTIVE_ITEM = 'I000';
    constant integer IID_INACTIVE_ITEM = 'I001';
    
    // When passive Spirit Touch procs, this effect is applied on the caster:
    constant string SFX_ON_CASTER = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl";
    constant string AP_ON_CASTER = "overhead";
    // Units that get mana restored get this effect as well:
    constant string SFX_ON_TARGET = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl";
    constant string AP_ON_TARGET = "chest";
    
    // Self-descriptive functions:
    function GetAoE(unit whichUnit) -> real
    {
        return 475.;
    }
    // NOTE: Mana is taken when the passive spirit touch procs.
    function GetManaCost(unit whichUnit) -> real
    {
        return 25.;
    }
    
    function GetManaRestored(unit whichUnit) -> real
    {
        return 50.;
    }
    
    function GetInterval(unit whichUnit) -> real
    {
        return 10.;
    }
    function GetMinShiftBound(unit whichUnit) -> real
    {
        return -3.;
    }
    function GetMaxShiftBound(unit whichUnit) -> real
    {
        return 3.;
    }
    
//===========================================================================
//       END OF CONFIGURATION
//===========================================================================
    group GROUP = CreateGroup();
    unit UNIT = null;
    
    constant integer TICKS_PER_SECOND = R2I(1. / TimedLoop_PERIOD);
    
    struct Data
    {
        unit u;
        item it;
        integer ticks;
        
        static method SetAlliesMana() // need better name
        {
            unit enum = GetEnumUnit();
            
            SetUnitState(enum, UNIT_STATE_MANA, /*
            */ GetUnitState(enum, UNIT_STATE_MANA) + GetManaRestored(UNIT));
            DestroyEffect(AddSpecialEffectTarget(SFX_ON_TARGET, enum, AP_ON_TARGET));
            
            enum = null;
        }
        
        static method GetAllies() -> boolean
        {
            return ( IsUnitAlly(GetFilterUnit(), GetOwningPlayer(UNIT)) && /*
            */  GetUnitState(GetFilterUnit(), UNIT_STATE_MANA) > 0. );
        }
        
        method applyRestoration()
        {
            real mana = GetUnitState(this.u, UNIT_STATE_MANA);
            real mana_req = GetManaCost(this.u);
            
            if ( mana >= mana_req )
            {
                UNIT = this.u;
                SetUnitState(UNIT, UNIT_STATE_MANA, mana - mana_req);
                DestroyEffect(AddSpecialEffectTarget(SFX_ON_CASTER, UNIT, AP_ON_CASTER));
                
                GroupEnumUnitsInRange(GROUP, GetUnitX(UNIT), GetUnitY(UNIT), /*
                */ GetAoE(UNIT), Condition(function thistype.GetAllies));
                ForGroup(GROUP, function thistype.SetAlliesMana); // need better name
            }
        }
        
        method onTimedLoop() -> boolean
        {
            if ( !UnitHasItem(this.u, this.it) )
                return TimedLoop_STOP;
            else
            {
                this.ticks -= 1;
                if ( this.ticks <= 0 )
                {
                    this.applyRestoration();
                    this.ticks = R2I(GetInterval(this.u) + /*
                    */ GetRandomReal(GetMinShiftBound(this.u), GetMaxShiftBound(this.u)));
                    this.ticks *= TICKS_PER_SECOND;
                }
            }
            return TimedLoop_CONTINUE;
        }
        
        module TimedLoop;
        
        static method create(unit whichUnit, item whichItem) -> thistype
        {
            thistype this = thistype.allocate();
            
            this.u = whichUnit;
            this.it = whichItem;
            this.ticks = R2I(GetInterval(this.u) + /*
            */ GetRandomReal(GetMinShiftBound(this.u), GetMaxShiftBound(this.u)));
            this.ticks *= TICKS_PER_SECOND;
            
            this.startTimedLoop();
            
            return this;
        }
    }
     
    
    function OnItemPickup() -> boolean
    {
        unit picker = GetTriggerUnit();
        item it = GetManipulatedItem();
        
        if ( GetItemTypeId(it) == IID_ACTIVE_ITEM )
            Data.create(picker, it);
            
        it = null; picker = null;
        return false;
    }
    
    
    function OnItemUse() -> boolean
    {
        item it = GetManipulatedItem();
        integer itemId = GetItemTypeId(it);
        unit user = GetTriggerUnit();
        
        if ( itemId == IID_ACTIVE_ITEM )
        {
            RemoveItem(it);
            it = CreateItem(IID_INACTIVE_ITEM, GetUnitX(user), GetUnitY(user));
            UnitAddItem(user, it);
        } else if ( itemId == IID_INACTIVE_ITEM )
        {
            RemoveItem(it);
            it = CreateItem(IID_ACTIVE_ITEM, GetUnitX(user), GetUnitY(user));
            UnitAddItem(user, it);
        }
        
        it = null; user = null;
        return false;
    }

    function onInit()
    {
        trigger trig = CreateTrigger();
        
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_USE_ITEM);
        TriggerAddCondition(trig, Condition(function OnItemUse));
        
        trig = CreateTrigger();
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_PICKUP_ITEM);
        TriggerAddCondition(trig, Condition(function OnItemPickup));
    }
}

//! endzinc

Last edited by HerrStanev : 03-10-2013 at 10:51 PM.
HerrStanev is offline   Reply With Quote
Old 03-06-2013, 04:27 PM   #5
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

Why are you using multiple timers when one timer can do it all?
Multiple timers for item ability are just begging for some abuse to create errors.

Use ticks on one timer to handle stuff.
Attach data to heroes, not timers.
Put heroes who have active item in a group.
On every timer tick check everybody in group, increase counters and if reached period do the spell.

Never remove this structs from heroes.
This way picking and dropping abuse will not have any effects because timer will just continue counting where it stopped for that hero the last time.
__________________
<nosignature />
cohadar is offline   Reply With Quote
Old 03-07-2013, 05:05 PM   #6
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

I feel like I've coded this really ugly.
Tell me if it needs changing.
Possibly should use an indexing system (PUI/AutoIndex)?

Collapse Zinc:
//! zinc

library ItemRequest01
{

    constant integer IID_ACTIVE_ITEM = 'I000';
    constant integer IID_INACTIVE_ITEM = 'I001';
    
    // When passive Spirit Touch procs, this effect is applied on the caster:
    constant string SFX_ON_CASTER = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl";
    constant string AP_ON_CASTER = "overhead";
    // Units that get mana restored get this effect as well:
    constant string SFX_ON_TARGET = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl";
    constant string AP_ON_TARGET = "chest";
    
    // Self-descriptive functions:
    function GetAoE(unit whichUnit) -> real
    {
        return 475.;
    }
    // NOTE: Mana is taken when the passive spirit touch procs.
    function GetManaCost(unit whichUnit) -> real
    {
        return 25.;
    }
    
    function GetManaRestored(unit whichUnit) -> real
    {
        return 50.;
    }
    
    function GetInterval(unit whichUnit) -> real
    {
        return 10.;
    }
    function GetMinShiftBound(unit whichUnit) -> real
    {
        return -3.;
    }
    function GetMaxShiftBound(unit whichUnit) -> real
    {
        return 3.;
    }
    
//===========================================================================
//       END OF CONFIGURATION
//===========================================================================
    keyword Data;
    Data STRUCT_DATA[];
    timer TIMER = CreateTimer();
    group GROUP = CreateGroup();
    unit UNIT = null;
    
    constant real TIMER_PERIOD = 0.025;
    constant integer TICKS_PER_SECOND = R2I(1. / TIMER_PERIOD);
    
    struct Data
    {
        unit u;
        integer ticks;
        
        static method SetAlliesMana() // need better name
        {
            unit enum = GetEnumUnit();
            
            SetUnitState(enum, UNIT_STATE_MANA, /*
            */ GetUnitState(enum, UNIT_STATE_MANA) + GetManaRestored(UNIT));
            DestroyEffect(AddSpecialEffectTarget(SFX_ON_TARGET, enum, AP_ON_TARGET));
            
            enum = null;
        }
        
        static method GetAllies() -> boolean
        {
            return ( IsUnitAlly(GetFilterUnit(), GetOwningPlayer(UNIT)) && /*
            */  GetUnitState(GetFilterUnit(), UNIT_STATE_MANA) > 0. );
        }
        
        method applyRestoration()
        {
            real mana = GetUnitState(this.u, UNIT_STATE_MANA);
            real mana_req = GetManaCost(this.u);
            
            if ( mana >= mana_req )
            {
                UNIT = this.u;
                SetUnitState(UNIT, UNIT_STATE_MANA, mana - mana_req);
                DestroyEffect(AddSpecialEffectTarget(SFX_ON_CASTER, UNIT, AP_ON_CASTER));
                
                GroupEnumUnitsInRange(GROUP, GetUnitX(UNIT), GetUnitY(UNIT), /*
                */ GetAoE(UNIT), Condition(function thistype.GetAllies));
                ForGroup(GROUP, function thistype.SetAlliesMana); // need better name
            }
        }
        
        method Callback()
        {
            this.ticks -= 1;
            if ( this.ticks <= 0 )
            {
                this.applyRestoration();
                this.ticks = R2I(GetInterval(this.u) + /*
                */ GetRandomReal(GetMinShiftBound(this.u), GetMaxShiftBound(this.u)));
                this.ticks *= TICKS_PER_SECOND;
            }
        }
        
        static method create(unit whichUnit) -> thistype
        {
            thistype this = thistype.allocate();
            
            this.u = whichUnit;
            this.ticks = R2I(GetInterval(this.u) + /*
            */ GetRandomReal(GetMinShiftBound(this.u), GetMaxShiftBound(this.u)));
            this.ticks *= TICKS_PER_SECOND;
            
            this.applyRestoration();
            
            return this;
        }
    }
    
    function TimerCallback()
    {
        ForGroup(GROUP, function()
        {
            unit enum = GetEnumUnit();
            
            if ( UnitHasItemOfTypeBJ(enum, IID_ACTIVE_ITEM) )
                STRUCT_DATA[GetHandleId(enum) - 0x100000].Callback();
            
            enum = null;
        });
    }
     
    
    function OnItemPickup() -> boolean
    {
        unit picker = GetTriggerUnit();
        item it = GetManipulatedItem();
        integer h2i = GetHandleId(picker) - 0x100000;
        
        if ( GetItemTypeId(it) == IID_ACTIVE_ITEM && STRUCT_DATA[h2i] == 0 )
        {
            STRUCT_DATA[h2i] = Data.create(picker);
            GroupAddUnit(GROUP, picker);
        }
            
        it = null; picker = null;
        return false;
    }
    
    
    function OnItemUse() -> boolean
    {
        item it = GetManipulatedItem();
        integer itemId = GetItemTypeId(it);
        unit user = GetTriggerUnit();
        
        if ( itemId == IID_ACTIVE_ITEM )
        {
            RemoveItem(it);
            it = CreateItem(IID_INACTIVE_ITEM, GetUnitX(user), GetUnitY(user));
            UnitAddItem(user, it);
        } else if ( itemId == IID_INACTIVE_ITEM )
        {
            RemoveItem(it);
            it = CreateItem(IID_ACTIVE_ITEM, GetUnitX(user), GetUnitY(user));
            UnitAddItem(user, it);
        }
        
        it = null; user = null;
        return false;
    }

    function onInit()
    {
        trigger trig = CreateTrigger();
        
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_USE_ITEM);
        TriggerAddCondition(trig, Condition(function OnItemUse));
        
        trig = CreateTrigger();
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_PICKUP_ITEM);
        TriggerAddCondition(trig, Condition(function OnItemPickup));
        
        TimerStart(TIMER, TIMER_PERIOD, true, function TimerCallback);
    }
}

//! endzinc

Last edited by HerrStanev : 03-10-2013 at 10:51 PM.
HerrStanev is offline   Reply With Quote
Old 03-07-2013, 07:55 PM   #7
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

integer h2i = GetHandleId(picker) - 0x100000;
Oh come on it is 2013 not 2003.

Use Autoindex, it is approved, PUI is not.
And I think more people will prefer it.

I also want you to break down that spell into 3 triggers.
One main with timer and 2 helper triggers with Pickup and Use events.

And moar comments ffs.
__________________
<nosignature />

Last edited by cohadar : 03-07-2013 at 07:56 PM.
cohadar is offline   Reply With Quote
Old 03-08-2013, 06:05 PM   #8
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

Don't know why you want it in 3 separate triggers.
But revisiting the code helped me spot a bug. Also, I found no need for structs, just an integer array counter works fine.
What I find irritating is that you'd have to edit raw codes at 3 different places.
Should I make them global in the main trigger so that the other two can use them?
I added a name to the item...

How do I put spoiler tags?
Collapse Zinc:
//! zinc

// Handles timer ticks and proper mana restoration.

library ReplenishmentSphere requires AutoIndex
{
    // Raw code of the active version of the item:
    public constant integer IID_REP_SPHERE_ACTIVE_ITEM = 'I000';
    public constant integer IID_REP_SPHERE_INACTIVE_ITEM = 'I001';
    
    // When passive Spirit Touch procs, this effect is applied on the caster:
    constant string SFX_ON_CASTER = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl";
    constant string AP_ON_CASTER = "overhead";
    // Units that get mana restored get this effect as well:
    constant string SFX_ON_TARGET = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl";
    constant string AP_ON_TARGET = "chest";
    
    // AoE of mana restoration when Spirit Touch procs:
    function GetAoE(unit whichUnit) -> real
    {
        return 475.;
    }
    // NOTE: Mana is taken from the owner of the item
    // when the passive spirit touch procs.
    function GetManaCost(unit whichUnit) -> real
    {
        return 25.;
    }
    
    // Mana restored when Spirit Touch procs:
    function GetManaRestored(unit whichUnit) -> real
    {
        return 50.;
    }
    
    // The following functions are used to calculate proc interval.
    // Formula is: Interval + Random( min_bound, max_bound ).
    // With current settings it can vary from 7 to 13 seconds.
    function GetInterval(unit whichUnit) -> real
    {
        return 10.;
    }
    function GetMinShiftBound(unit whichUnit) -> real
    {
        return -3.;
    }
    function GetMaxShiftBound(unit whichUnit) -> real
    {
        return 3.;
    }
    
//===========================================================================
//       END OF CONFIGURATION
//===========================================================================
    // ReplenishmentSphere_GROUP contains all units that have obtained the 
    // active version of the item. Units in this group get checked periodically
    // if they have the item, and if they do their timer runs until it hits 0.
    // On 0 timer ticks the passive Spirit Touch procs.
    public group ReplenishmentSphere_GROUP = CreateGroup();
    
    // UNIT is an argument for global passing to Condition() filter.
    // GROUP is a group used in ForGroup() calls.
    group GROUP = CreateGroup();
    unit UNIT = null;
    
    // TIMER runs every TIMER_PERIOD. Used to subtract ticks from units.
    timer TIMER = CreateTimer();
    // Every unit with the Replenishment Sphere item contains this property:
    integer UNIT_TICKS[];
    
    constant real TIMER_PERIOD = 0.025;
    constant integer TICKS_PER_SECOND = R2I(1. / TIMER_PERIOD);
    
    // Main restoration function on Spirit Touch procs:
    function SetAlliesMana() 
    {
        unit enum = GetEnumUnit();
            
        SetUnitState(enum, UNIT_STATE_MANA, /*
        */ GetUnitState(enum, UNIT_STATE_MANA) + GetManaRestored(UNIT));
        DestroyEffect(AddSpecialEffectTarget(SFX_ON_TARGET, enum, AP_ON_TARGET));
            
        enum = null;
    }
    
    // Gets valid units to restore their mana on Spirit Touch proc:
    function GetAllies() -> boolean
    {
        return ( IsUnitAlly(GetFilterUnit(), GetOwningPlayer(UNIT)) && /*
        */  GetUnitState(GetFilterUnit(), UNIT_STATE_MANA) > 0. );
    }

    
    // Handles ticks subtraction and restoration when ticks for 
    // certain units are less than 0 (time to restore mana). Also
    // checks if units with 0 ticks have enough mana to proc Spirit Touch.
    function TimerCallback()
    {
        ForGroup(ReplenishmentSphere_GROUP, function()
        {
            unit enum = GetEnumUnit();
            
            // Have some sort of active version?
            if ( UnitHasItemOfTypeBJ(enum, IID_REP_SPHERE_ACTIVE_ITEM) )
            {
                UNIT_TICKS[GetUnitId(enum)] -= 1;
                
                // Is it time to proc Spirit Touch? Is there enough mana to proc?
                if ( UNIT_TICKS[GetUnitId(enum)] <= 0 && /*
                */ GetUnitState(enum, UNIT_STATE_MANA) >= GetManaCost(enum) )
                {
                    UNIT = enum; // UNIT is for passing in Enum filter.
                    SetUnitState(UNIT, UNIT_STATE_MANA, GetUnitState(UNIT, UNIT_STATE_MANA) - GetManaCost(UNIT));
                    DestroyEffect(AddSpecialEffectTarget(SFX_ON_CASTER, UNIT, AP_ON_CASTER));
                            
                    GroupEnumUnitsInRange(GROUP, GetUnitX(UNIT), GetUnitY(UNIT), /*
                    */ GetAoE(UNIT), Condition(function GetAllies));
                    ForGroup(GROUP, function SetAlliesMana); 
                    
                    // Reset ticks to random period.
                    UNIT_TICKS[GetUnitId(enum)] = R2I(GetInterval(enum) + /*
                    */ GetRandomReal(GetMinShiftBound(enum), GetMaxShiftBound(enum))) * TICKS_PER_SECOND;
                }
            }
            
            enum = null;
        });
    }

    function onInit()
    {
        TimerStart(TIMER, TIMER_PERIOD, true, function TimerCallback);
    }
}

//! endzinc
Collapse Zinc:
//! zinc

// Adds units that have picked the active version of the item in a group.

library ReplenishmentSpherePick requires ReplenishmentSphere
{
    // Raw code of the active version of the item:
    constant integer IID_ACTIVE_ITEM = IID_REP_SPHERE_ACTIVE_ITEM;
    
    function Conditions() -> boolean
    {
        return ( GetItemTypeId(GetManipulatedItem()) == IID_ACTIVE_ITEM );
    }
    
    function Actions()
    {
        unit picker = GetTriggerUnit();
        
        // First time acquiring the Replenishment Sphere item?
        if ( !IsUnitInGroup(picker, ReplenishmentSphere_GROUP) )
            GroupAddUnit(ReplenishmentSphere_GROUP, picker);

        picker = null;
    }
    
    function onInit()
    {
        trigger trig = CreateTrigger();
        
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_PICKUP_ITEM);
        TriggerAddCondition(trig, Condition(function Conditions));
        TriggerAddAction(trig, function Actions);
    }
}

//! endzinc
Collapse Zinc:
//! zinc

// Handles switching between active and inactive version of the item.

library ReplenishmentSphereUse requires ReplenishmentSphere
{
    // Raw code of the active version of the item:
    constant integer IID_ACTIVE_ITEM = IID_REP_SPHERE_ACTIVE_ITEM;
    // Raw code of the inactive version of the item:
    constant integer IID_INACTIVE_ITEM = IID_REP_SPHERE_INACTIVE_ITEM;
    
    function Conditions() -> boolean
    {
        return ( GetItemTypeId(GetManipulatedItem()) == IID_ACTIVE_ITEM || /*
        */       GetItemTypeId(GetManipulatedItem()) == IID_INACTIVE_ITEM );
    }
    
    function Actions()
    {
        unit user = GetTriggerUnit();
        item it = GetManipulatedItem();
        integer itemId = GetItemTypeId(it);
        
        RemoveItem(it);
        // Determines which version was used (active or inactive)
        // and adds swaps it with the opposite one. 
        if ( itemId == IID_ACTIVE_ITEM )
            it = CreateItem(IID_INACTIVE_ITEM, GetUnitX(user), GetUnitY(user));
        else
            it = CreateItem(IID_ACTIVE_ITEM, GetUnitX(user), GetUnitY(user));
        UnitAddItem(user, it);
            
        user = null; it = null;
    }
    
    function onInit()
    {
        trigger trig = CreateTrigger();
        
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_USE_ITEM);
        TriggerAddCondition(trig, Condition(function Conditions));
        TriggerAddAction(trig, function Actions);
    }
}

//! endzinc

Last edited by HerrStanev : 03-10-2013 at 10:51 PM.
HerrStanev is offline   Reply With Quote
Old 03-08-2013, 06:15 PM   #9
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

Quote:
Originally Posted by HerrStanev
Don't know why you want it in 3 separate triggers.
But revisiting the code helped me spot a bug.
You answered your own question already.
I might also add that any software developer on the surface of earth would laugh at that question.
Divide and Conquer is the single most important skill for a person who wants to live by his code.

Quote:
Originally Posted by HerrStanev
What I find as irritating is that you'd have to edit raw codes at 3 different places.
Should I make them global in the main trigger so that the other two can use them?
Of course you should, but don't make them global but public.
Made a mistake there, forgot we were working in a language without static imports, global is fine.

EDIT2:
Also if you really want to do it properly, that timer should not run when noone is using the active items. Start timer only when it is first time needed and use PauseTimer and ResumeTimer functions later.
__________________
<nosignature />

Last edited by cohadar : 03-08-2013 at 06:29 PM.
cohadar is offline   Reply With Quote
Old 03-08-2013, 06:33 PM   #10
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

Updated the previous post with the global version.

Quote:
Also if you really want to do it properly, that timer should not run when noone is using the active items. Start timer only when it is first time needed and use PauseTimer and ResumeTimer functions later.
Well, I can turn it on when the first active item in the game is acquired.
But I have no idea how to approach this with Pause and Resume. :(
HerrStanev is offline   Reply With Quote
Old 03-08-2013, 07:16 PM   #11
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

You create and activate the timer the first time it is needed.
This is a very often used technique in software development:
http://en.wikipedia.org/wiki/Lazy_initialization

Than you maintain a counter of active items that are equipped on a hero.

When active item is equipped, you increase a counter.
When active item is dropped, you decrease a counter.
When passive item is changed to active you increase a counter.
When active item is changed to passive you decrease a counter.
When passive item is picked or dropped you do NOT change the counter.

When counter becomes equal to 1, you ResumeTimer,
When counter becomes equal to 0, you PauseTimer.

In programming counters that can be zero or higher and activate and deactivate something on 1 and 0, are called Semaphores.
http://en.wikipedia.org/wiki/Semapho...programming%29

TIP: Use the LOG library from UHS to debug your code while you work.
Log the timer creation and every counter change so you can track what is going on.
__________________
<nosignature />

Last edited by cohadar : 03-08-2013 at 07:19 PM.
cohadar is offline   Reply With Quote
Old 03-09-2013, 01:41 PM   #12
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

Just ran into something weird.
If I resume a previously paused periodic timer it only runs TWO times.
So what I have to do is just call TimerStart and make the callback function global, zzzzzz.

Maybe I'm doing something wrong but try this out, it just prints twice for me:
Collapse Zinc:
//! zinc

library test
{
    boolean b = false;
    timer tim = CreateTimer();
    
    function Callback()
    {
        BJDebugMsg("abc");
    }
    
    function onInit()
    {
        TimerStart(tim, 0.33, true, function Callback);
        PauseTimer(tim);
        TimerStart(CreateTimer(), 3., true, function()
        {
            b = !b;
            if ( b )
                ResumeTimer(tim);
            else
                PauseTimer(tim);
        } );
    }
}

//! endzinc
HerrStanev is offline   Reply With Quote
Old 03-09-2013, 05:44 PM   #13
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

Collapse Zinc:
//! zinc

library test
{
    boolean b = false;
    timer tim = CreateTimer();
    integer counter = 0;
    
    function Callback()
    {
        counter = counter + 1;
        BJDebugMsg(I2S(counter));
    }
    
    function onInit()
    {
        TimerStart(tim, 0.33, true, function Callback);
        PauseTimer(tim);
        TimerStart(CreateTimer(), 3., true, function()
        {
            b = !b;
            if ( b )
                ResumeTimer(tim);
            else
                PauseTimer(tim);
        } );
    }
}

//! endzinc

This prints 12 34 56 78, all normal.
Dunno what your problem is. Are you using NewGen 5e ?
__________________
<nosignature />

Last edited by cohadar : 03-09-2013 at 05:44 PM.
cohadar is offline   Reply With Quote
Old 03-09-2013, 06:15 PM   #14
HerrStanev
User
 
HerrStanev's Avatar
 
Join Date: Mar 2013
Posts: 49

HerrStanev has little to show at this moment (8)

Default

Well yes, that's the issue. It should print "12345678", not "12, 34, 56, 78".
After all, the "tim" timer should run 9 times (3 / 0.33), then get paused for 3 seconds, then run another 9 times during the 3 seconds it is resumed.

The same thing happens in the item script. The 0.025 timer after being resumed runs only two times, then just stops... It's as if the ResumeTimer function resets the timer to non-periodic.
Or maybe I'm doing some total bullshit.
Yes, I'm with 1.5e NewGen.

Last edited by HerrStanev : 03-09-2013 at 06:16 PM.
HerrStanev is offline   Reply With Quote
Old 03-09-2013, 06:24 PM   #15
cohadar
master of fugue
 
cohadar's Avatar
 
Join Date: Jun 2007
Posts: 2,558

Submissions (5)

cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)cohadar is a jewel in the rough (247)

Default

No it is just another error in jass.
Please report it here: http://www.wc3c.net/showthread.php?t=80693

This works ok:
Collapse Zinc:
//! zinc

library test
{
    boolean b = false;
    timer tim = CreateTimer();
    integer counter = 0;
    
    function Callback()
    {
        counter = counter + 1;
        BJDebugMsg(I2S(counter));
    }
    
    function onInit()
    {
        TimerStart(tim, 0.33, true, function Callback);
        PauseTimer(tim);
        TimerStart(CreateTimer(), 3., true, function()
        {
            b = !b;
            if ( b )
                //ResumeTimer(tim);
                TimerStart(tim, 0.33, true, function Callback);
            else
                PauseTimer(tim);
                counter = 0;  // reset counter
        } );
    }
}

//! endzinc
__________________
<nosignature />
cohadar is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 08:04 PM.


Donate

Affiliates
The Hubb http://bylur.com - Warcraft, StarCraft, Diablo and DotA Blog & Forums The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2013, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services