• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Simple bounce ball

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Hero throws a bounce ball which will devide in 3 another bounce balls after fall

Settings
JASS:
scope BounceScope initializer Init

    globals
        /* BType is a value that shows how many times can ball make another balls, for example: 
        Start ball has BType = 4, so he will do balls: 1 - 3 - 9 - 27, (four times), every new balls will have BType - 1*/
        private integer SpellId = 'A000'
        private string SpellEffect = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl" // spell effect if BType will more then 0
        private string SpellEffectOnDeath = "Abilities\\Spells\\Other\\Volcano\\VolcanoDeath.mdl" // spell effect when BType == 0
        private real DistancePerBallType = 300 // as more this value then more will be move distance of new balls
        private real DamageMultiply = 40 // this value multiply on BType value (damage value)
        private real FlyHeightPerBallType = 40 // this value changes balls fly height per BType (100 + BType * this value)
        //////Timer period//////
        private real TimerTick = 0.03 // timer period
        //////////////////////
    endglobals

    struct Bounce
        private static timer t = CreateTimer()
        private static integer m[]
        private static integer MUI = -1
        unit a
        unit ball
        real angle
        real d
        real dmg
        real x
        real y
        real speed
        real count
        real time
        real distancePlus
        integer BType

        private method StopAction takes integer j returns nothing
            set m[j] = m[MUI]
            set MUI = MUI - 1
            if MUI == -1 then
                call PauseTimer(t)
            endif
        endmethod


        private static method Bounce_Loop takes nothing returns nothing
            local thistype this
            local integer i = 0
            local real dx
            local real dy
            local real height
            local group g
            local unit F
            local real checkX
            local real checkY
            loop
                exitwhen i > MUI
                set this = m[i]
                set checkX = GetUnitX(ball) + speed * Cos(angle)
                set checkY = GetUnitY(ball) + speed * Sin(angle)
                if GetRectMinX(bj_mapInitialPlayableArea) <= checkX and checkX <= GetRectMaxX(bj_mapInitialPlayableArea) and GetRectMinY(bj_mapInitialPlayableArea) <= checkY and checkY <= GetRectMaxY(bj_mapInitialPlayableArea)
                    set count = count + TimerTick
                    call SetUnitX(ball, checkX)
                    call SetUnitY(ball, checkY)
                    set dx = x - GetUnitX(ball)
                    set dy = y - GetUnitY(ball)
                    set height = (4 * (100 + FlyHeightPerBallType * BType) / d) * (d - SquareRoot(dx * dx + dy * dy)) * (SquareRoot(dx * dx + dy * dy) / d)
                    call SetUnitFlyHeight(ball, height, 0)
                    if count >= time then
                        set g = CreateGroup()
                        call GroupEnumUnitsInRange(g, GetUnitX(ball), GetUnitY(ball), 80 + (30 * BType), null)
                        set BType = BType - 1
                        loop
                            set F = FirstOfGroup(g)
                            exitwhen F == null
                            if IsUnitEnemy(F, GetOwningPlayer(a)) and not IsUnitType(F, UNIT_TYPE_DEAD) then
                                call UnitDamageTarget(a, F, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DIVINE, null)
                            endif
                            call GroupRemoveUnit(g, F)
                            set F = FirstOfGroup(g)
                        endloop
                        call DestroyGroup(g)
                        if BType != 0 then
                            call DestroyEffect(AddSpecialEffect(SpellEffect, GetUnitX(ball), GetUnitY(ball)))
                            call SetUnitFlyHeight(ball, 0, 0)
                            set d = distancePlus + (BType * 60)
                            set dmg = DamageMultiply * BType
                            set count = 0
                            call SetUnitScale(ball, BType / 1.5, BType / 1.5, BType / 1.5)
                            set x = GetUnitX(ball) + d * Cos(angle)
                            set y = GetUnitY(ball) + d * Sin(angle)
                            set time = (0.4 + BType * 0.1)
                            set speed = d / time * TimerTick
                            call Bounce_Start(a, angle + (30 * bj_DEGTORAD), BType, GetUnitX(ball), GetUnitY(ball), distancePlus, false)
                            call Bounce_Start(a, angle - (30 * bj_DEGTORAD), BType, GetUnitX(ball), GetUnitY(ball), distancePlus, false)
                        else
                            call DestroyEffect(AddSpecialEffect(SpellEffectOnDeath, GetUnitX(ball), GetUnitY(ball)))
                            RemoveUnit(ball)
                            set ball = null
                            set a = null
                            call StopAction(i)
                            call destroy()
                        endif
                    endif
                else
                    call DestroyEffect(AddSpecialEffect(SpellEffectOnDeath, GetUnitX(ball), GetUnitY(ball)))
                    RemoveUnit(ball)
                    set ball = null
                    set a = null
                    call StopAction(i)
                    call destroy()
                endif
                set i = i + 1
            endloop
        endmethod


        public static method Bounce_Start takes unit Caster, real NewAngle, integer NewBType, real NewX, real NewY, real NewDPlus, boolean IsSpell returns nothing
            local thistype this = thistype.create()
            local real dx
            local real dy
            set MUI = MUI + 1
            set m[MUI] = this
            set a = Caster
            set BType = NewBType
            set angle = NewAngle
            set distancePlus = NewDPlus
            set d = distancePlus + (BType * 60)
            set dmg = DamageMultiply * BType
            set count = 0
            set ball = CreateUnit(GetOwningPlayer(a), 'h000', NewX, NewY, angle)
            set dx = GetSpellTargetX() - GetUnitX(ball)
            set dy = GetSpellTargetY() - GetUnitY(ball)
            call UnitAddAbility(ball, 'Arav')
            call UnitRemoveAbility(ball, 'Arav')
            call SetUnitScale(ball, NewBType / 1.5, NewBType / 1.5, NewBType / 1.5)
            if not IsSpell then
                set x = GetUnitX(ball) + d * Cos(angle)
                set y = GetUnitY(ball) + d * Sin(angle)
            else
                set d = SquareRoot(dx * dx + dy * dy)
                set distancePlus = DistancePerBallType / (300 / d)
                set x = GetUnitX(ball) + d * Cos(angle)
                set y = GetUnitY(ball) + d * Sin(angle)
            endif
            set time = 0.4 + BType * 0.1
            set speed = d / time * TimerTick
            if MUI == 0 then
                call TimerStart(t, TimerTick, true, function thistype.Bounce_Loop)
            endif
        endmethod
    endstruct

    private function Spell_Start_Cond takes nothing returns boolean
    local unit a
    local real angle
        if GetSpellAbilityId() == SpellId then
            set a = GetSpellAbilityUnit()
            set angle = Atan2(GetSpellTargetY() - GetUnitY(a), GetSpellTargetX() - GetUnitX(a))
            call Bounce.Bounce_Start(a, angle, 4, GetUnitX(a), GetUnitY(a), 0, true)
            set a = null
        endif
        return false
    endfunction


    private function Init takes nothing returns nothing
        local trigger Cast = CreateTrigger( )
        local integer index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(Cast, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set index = index + 1
            exitwhen index == 16
        endloop
        call TriggerAddCondition(Cast, Condition(function Spell_Start_Cond))
        set Cast = null
    endfunction
endscope

This struct uses itself but with different settings for new balls (speed, time, distance, height) so maybe it'll be usefull for someone
[Fixed]
1. Added to settings 2 effects : final effect and effect after fall
2. Set timer period to .03
3. Added spell id to settings
4. Now use Trigg Add Cond (not trigg add action)

P.S sorry for my bad english =(

Keywords:
bounce, ball
Contents

Bounce ball War3I4i (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 12:33, 23rd Apr 2014 BPower: Looks cool according to the picture, but there are some things which have to improve. link Need Fix for now. Feel free to ask if you have any questions.

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

12:33, 23rd Apr 2014
BPower:
Looks cool according to the picture, but there are some things which have to improve. link
Need Fix for now. Feel free to ask if you have any questions.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Never use TriggerAddAction use TriggerAddCondition and run everything from there.
Use a timer of .03125
When using vJass and structs simply make the whole spell in the struct. It is much easier to manage.
The spell id should be easily configurable.
The special effects should be easily configurable.
You should filter out dead / removed units.
The time should be easily configurable.

Look at some of my spells. They are in vJass / Jass. Look at all the configurations there.
 
Level 5
Joined
Aug 23, 2013
Messages
42
Конечно же я понимаю русс)
And now about your advices:

1. Use a timer of .03125 - oh okay, i'll fix it soon
2. When using vJass and structs simply make the whole spell in the struct. It is much easier to manage. - So you want to tell me that all my spell not in structure or what?
3. Spell id i'll make in global variable, ty
4. Effects too
5. The time should be easily configurable - time of what?

[Fixed]
1. Added to settings 2 effects : final effect and effect after fall
2. Set timer period to .03
3. Added to settings spell id
4. Now use trigg add condition (not add action)
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
2. When using vJass and structs simply make the whole spell in the struct. It is much easier to manage. - So you want to tell me that all my spell not in structure or what?

These are part of the spell and not in the struct.
JASS:
    private function Spell_Start_Cond takes nothing returns boolean
        return GetSpellAbilityId() == SpellId
    endfunction

    private function Spell_Start takes nothing returns nothing
        local unit a = GetSpellAbilityUnit()
        local real angle = Atan2(GetSpellTargetY() - GetUnitY(a), GetSpellTargetX() - GetUnitX(a))
        call Bounce.Bounce_Start(a, angle, 4, GetUnitX(a), GetUnitY(a), 0, true)
        set a = null
    endfunction


    private function Init takes nothing returns nothing
        local trigger Cast = CreateTrigger( )
        local integer index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(Cast, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set index = index + 1
            exitwhen index == 16
        endloop
        call TriggerAddAction( Cast, function Spell_Start )
        call TriggerAddCondition(Cast, Condition(function Spell_Start_Cond))
        set Cast = null
    endfunction

5. The time should be easily configurable - time of what?

By the time I meant the timer.
 
Level 5
Joined
Aug 23, 2013
Messages
42
Of course this part of spell is not in the struct because it's spell starting with some arguments, Init function it's a scope initializer function, this part don't need to be in struct)
Also i'm fixed code and now don't use trigg add action, thanks for your advices
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
- GetSpellAbilityUnit() could be GetTriggerUnit()
- methods are public by default, if not declared as private.
- Store 'h000' into an integer variable. In other maps 'h000' might be a totally different unit type, hence has to be part of the configuration block.
- local unit F --> local unit u
- There is a missing "call" statement here RemoveUnit(ball)
- Don't use magic numbers, a good rule is that everything except 1, 0, -1 should be configurable and/or stored into variables.
- GetRectMinX(bj_mapInitialPlayableArea), etc ... should be stored on init or you can use a library like WorldBounds/SentinelBounds as requirement for this spell.
- In this case ballx/y is already stored into checkX/Y.
JASS:
                    set dx = x - GetUnitX(ball)
                    set dy = y - GetUnitY(ball)
You have multiple needless GetUnitX/Y function calls, as those values are already stored into local variables.

- For group enumerations create one global group on map init instead of multiple local ones.
- Your identation is screwed at some points of the script code.
- In general the convention for methods should be camelCase and functions PascalCase
- You may explain the IsSpell condition.
- Chose reasonable variables names instead of random one char names like a.
Furthermore please stick to the JPAG convention, it is mandatory for submitted resources in JASS and vJass.

Feel free to ask for help if you need it.
 
Level 5
Joined
Aug 23, 2013
Messages
42
Sorry but i'll do all changes tomorrow, now about fixes:
1. ok, i'll change it to GetTriggerUnit()
2. thanks for this advice
3. sorry, i frogoted to save unit id in global variable
4. i'll not change unit F to unit u bcause i'm always use F (as FirstOfGroup())
5. hm... okay
6. i need to check this distance to make a parabola height
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Unit F associated for me with FirstOfGroup function so i use unit F :grin:

Grin all you want. I was just telling you convention. Keep being rude like that though and not many people will want to bother helping you.

You set firstofgroup here twice for no reason.
JASS:
                        loop
                            set F = FirstOfGroup(g)
                            exitwhen F == null
                            if IsUnitEnemy(F, GetOwningPlayer(a)) and not IsUnitType(F, UNIT_TYPE_DEAD) then
                                call UnitDamageTarget(a, F, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_DIVINE, null)
                            endif
                            call GroupRemoveUnit(g, F)
                            set F = FirstOfGroup(g)
                        endloop

Also timer should be 0.03125
.03 is for GUI triggers.

You need to null local groups.
You should make a global group and create it only once. Creating and destroying groups is very costly.
 
Top