1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. And she's bought a stairway to heaven. The 6th Special Effect Contest Results are here.
    Dismiss Notice
  3. Seek unity between the elements in the 22nd Terraining Contest Poll.
    Dismiss Notice
  4. Seize the moment! The 18th Mini Mapping Contest has commenced.
    Dismiss Notice
  5. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Prediction - v1.0

Submitted by Mr_Bean
This bundle is marked as approved. It works and satisfies the submission rules.
[​IMG]

Description


[​IMG]
Active: Using a process of complex calculations, the Mage predicts an area where the target unit will be in 2 seconds. If he is correct and the target is inside the area, it takes a large amount of damage. If the target is outside the area, it will get hit by a mana flare which burns mana, depending how far it was from the area. The greater the distance, the less mana is burned.

Level 1: 150AoE; 150 damage; up to 100 mana burned
Level 2: 175AoE; 250 damage; up to 150 mana burned
Level 3: 200AoE; 350 damage; up to 200 mana burned

Cooldown: 13 seconds.
Mana cost: 100/125/150.


Requirements


- JASS NewGen Pack
- TimerUtils by Vexorian
- xebasic, xefx, xemissile by Vexorian
- [OPTIONAL] SpellEffectEvent by Bribe

Links for the above systems can be found in the code.

The Code



vJASS
Code (vJASS):
library Prediction /*
****************************************************************************************************
*
*   P R E D I C T I O N
*
*       ~ version 1.0
*       ~ coded by Mr_Bean
*
****************************************************************************************************
*
*    */
requires /*
*       ¯¯¯¯¯¯¯¯
*           */
TimerUtils,                  /* http://www.wc3c.net/showthread.php?t=101322
*           */
xemissile,                   /* http://www.wc3c.net/showthread.php?t=101150
*
*       Optional
*       ¯¯¯¯¯¯¯¯
*           */
optional SpellEffectEvent    /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193
*
****************************************************************************************************
*
*       Installation
*       ¯¯¯¯¯¯¯¯¯¯¯¯
*   - Make sure you've got (and installed properly) the above systems.
*   - Copy the custom ability. Set SPELL_ID (below) to the raw ID of this ability.
*   - Go through the configurables and change what you want to.
*
****************************************************************************************************
*
*       Configuration
*       ¯¯¯¯¯¯¯¯¯¯¯¯¯
*/

    globals
   
        //==================================================
        //=== RAW IDS
        private constant integer SPELL_ID       = 'A000'    // Raw ID of the ability.
       
        //==================================================
        //=== GENERAL
        private constant real    INITIAL_DELAY  = 0.03      // Delay between cast and prediction. Shouldn't need to be changed.
       
        //==================================================
        //=== CENTRE AREA EFFECT CONFIGURATION
        // This effect is created at the centre of the prediction area.
        private constant real    L_FX_SCALE     = 1.0       // Model scale.
        private constant real    L_FX_HEIGHT    = 25.0      // Height.
       
        // Model path:
        private constant string  INDICATOR_FX   = "Abilities\\Spells\\Items\\VampiricPotion\\VampPotionCaster.mdl"
       
        //==================================================
        //=== PERIMITER AREA EFFECT CONFIGURATION
        // Multiple of these effects are created around the perimeter of the prediction area.
        private constant integer NUM_EFFECTS    = 8         // Number of effects.
        private constant real    EFFECT_SCALE   = 1.0       // Model scale.
        private constant real    EFFECT_HEIGHT  = 25.0      // Height.
       
        // Model path:
        private constant string  INDICATOR_ART  = "Abilities\\Weapons\\BloodElfMissile\\BloodElfMissile.mdl"
       
        //==================================================
        //=== EXPLOSION CONFIGURATION
       
        // Created at prediction area when the time expires:
        private constant string  EXPLODE_EFFECT = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
       
        // Created at prediction area when the target is in the area:
        private constant string  HIT_EFFECT     = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
       
        // Created at prediction area when the target is not in the area:
        private constant string  MISS_EFFECT    = "Abilities\\Spells\\Human\\Polymorph\\PolyMorphTarget.mdl"
       
        //==================================================
        //=== MANA MISSILE CONFIGURATION
        private constant real    MAX_DISTANCE   = 1000.0    // No mana is burned if the target is this far from the prediction area.
        private constant real    MISSILE_SCALE  = 1.0       // Scale.
        private constant real    MISSILE_SPEED  = 750.0     // Speed.
        private constant real    MISSILE_ARC    = 0.2       // Arc.
        private constant real    MISSILE_HEIGHT = 50.0      // Height.
       
        // Model:
        private constant string  MISSILE_ART    = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
       
        // Effect created on target when mana is burned:
        private constant string  MANA_BURN_FX   = "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl"
       
        // Attachment point for the above effect:
        private constant string  BURN_FX_ATTACH = "origin"
       
        //==================================================
        //=== FLOATING TEXT CONFIGURATION
        private constant boolean DISPLAY_TEXT   = true      // Display floating text showing damage dealt?
        private constant real    TEXT_ANGLE     = 90.0      // Movement angle.
        private constant real    TEXT_SPEED     = 64.0      // Movement speed.
        private constant real    TEXT_OFFSET    = 50.0      // Height above target.
        private constant real    TEXT_DURATION  = 2.0       // Duration.
        private constant real    TEXT_FADEPOINT = 1.0       // Fade time.
        private constant real    TEXT_HEIGHT    = 0.023     // Size.
       
        // Colour of text when showing damage:
        private constant string  COLOUR_DAMAGE  = "|cffff0000"
       
        // Colour of text when showing mana burned:
        private constant string  COLOUR_MANA    = "|cff8000FF"
    endglobals
   
    //==================================================
    //=== PREDICTS TARGET'S POSITION IN THIS MANY SECONDS
    private function GetDelay takes integer level returns real
        return 2.0
    endfunction
   
    //==================================================
    //=== HIT AREA OF EFFECT
    private function GetArea takes integer level returns real
        return (25.0 * level) + 125.0
    endfunction
   
    //==================================================
    //=== DAMAGE DEALT ON HIT
    private function GetDamage takes integer level returns real
        return (100.0 * level) + 50.0
    endfunction
   
    //==================================================
    //=== MANA BURNED ON MISS
    private function GetManaBurn takes integer level returns real
        return 100.0 * level
    endfunction
   
    //==================================================
    //=== DAMAGE FUNCTION SETUP
    // This is if you want to change attack/damage types or if you use a custom
    // function for damage detection or something similar.
    private function DealDamage takes unit source, unit target, real amount returns nothing
        call UnitDamageTarget(source, target, amount, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
    endfunction
   
/***************************************************************************************************
*
*   END OF CONFIGURATION AND DOCUMENTATION
*
***************************************************************************************************/


    globals
        private constant real TEXT_X_VELOCITY = (Cos(TEXT_ANGLE * bj_DEGTORAD) * TEXT_SPEED) * 0.071 / 128.0
        private constant real TEXT_Y_VELOCITY = (Sin(TEXT_ANGLE * bj_DEGTORAD) * TEXT_SPEED) * 0.071 / 128.0
    endglobals
   
    static if (DISPLAY_TEXT) then
        private function DisplayText takes unit target, string text returns nothing
            local texttag tt
           
            if (IsVisibleToPlayer(GetUnitX(target), GetUnitY(target), GetLocalPlayer())) then
                set tt = CreateTextTag()
                call SetTextTagText(tt, text, TEXT_HEIGHT)
                call SetTextTagPosUnit(tt, target, TEXT_OFFSET)
                call SetTextTagVelocity(tt, TEXT_X_VELOCITY, TEXT_Y_VELOCITY)
                call SetTextTagPermanent(tt, false)
                call SetTextTagLifespan(tt, TEXT_DURATION)
                call SetTextTagFadepoint(tt, TEXT_FADEPOINT)
                call SetTextTagVisibility(tt, true)
                set tt = null
            endif
        endfunction
    endif

    private struct ManaMissile extends xemissile
        real amount
       
        private method onHit takes nothing returns nothing
            local real mana
           
            if (not IsUnitType(targetUnit, UNIT_TYPE_DEAD) /*
                */
and not IsUnitType(targetUnit, UNIT_TYPE_MAGIC_IMMUNE) /*
                */
and not IsUnitType(targetUnit, UNIT_TYPE_STRUCTURE)) then
               
                set mana = GetUnitState(targetUnit, UNIT_STATE_MANA)
               
                if (mana > 0.0) then
                    call SetUnitState(targetUnit, UNIT_STATE_MANA, mana - amount)
                    call DestroyEffect(AddSpecialEffectTarget(MANA_BURN_FX, targetUnit, BURN_FX_ATTACH))
                    static if (DISPLAY_TEXT) then
                        call DisplayText(targetUnit, COLOUR_MANA + "-" + I2S(R2I(amount)) + "|r")
                    endif
                endif
            endif
        endmethod
   
        static method create takes real sx, real sy, unit t, real amt returns thistype
            local thistype this = allocate(sx, sy, 0.0, GetUnitX(t), GetUnitY(t), MISSILE_HEIGHT)
           
            set amount = amt
            set targetUnit = t
            set fxpath = MISSILE_ART
            set scale = MISSILE_SCALE
            call launch(MISSILE_SPEED, MISSILE_ARC)
           
            return this
        endmethod
   
    endstruct
   
    // I had to write this because I can't set xefx objects to be
    // visible to only certain players.
    private struct Effect
        unit dummy
        effect fx
       
        method destroy takes nothing returns nothing
            call DestroyEffect(fx)
            set dummy = null
            set fx = null
            call deallocate()
        endmethod
       
        static method create takes player p, real x, real y, real s, real h, string e returns thistype
            local thistype this = allocate()
           
            set dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), XE_DUMMY_UNITID, x, y, 0.0)
            call SetUnitScale(dummy, s, s, s)
            set fx = AddSpecialEffectTarget(e, dummy, "origin")
            call ShowUnit(dummy, IsPlayerAlly(GetLocalPlayer(), p))
           
            if (h != 0.0) then
                call UnitAddAbility(dummy, 'Amrf')
                call UnitRemoveAbility(dummy, 'Amrf')
                call SetUnitFlyHeight(dummy, h, 99999.0)
            endif
           
            return this
        endmethod
   
    endstruct
   
    private type fxList extends Effect array[NUM_EFFECTS]
   
    private struct Prediction extends array // I made it an array struct because I wanted to try it :)
        static constant real ANGLE_DIV = (2.0 * bj_PI) / NUM_EFFECTS
        //-----
        static integer numInstances = 0
        static integer numRecycles  = 0
        //-----
        static integer array recycled
        //-----
        unit    caster
        unit    target
        integer level
        real    tx
        real    ty
        real    area
        fxList  fx
        Effect  locFx
        //-----
       
        /**
         * Removes the area effects and cleans up.
         */

        private method destroy takes nothing returns nothing
            local integer i = 0
           
            loop
                exitwhen i == NUM_EFFECTS
                call fx[i].destroy()
                set i = i + 1
            endloop
           
            call fx.destroy()
            call locFx.destroy()
            set caster = null
            set target = null
            set recycled[numRecycles] = this
            set numRecycles = numRecycles + 1
        endmethod
       
        /**
         * Checks to see whether the target is in the area. Damages or burns
         * mana accordingly.
         */

        private static method explode takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real newX = GetUnitX(target)
            local real newY = GetUnitY(target)
            local real distance = SquareRoot((newX - tx) * (newX - tx) + (newY - ty) * (newY - ty))
           
            call ReleaseTimer(GetExpiredTimer())
            call DestroyEffect(AddSpecialEffect(EXPLODE_EFFECT, tx, ty))
           
            // Target is in area:
            if (distance <= area) then
                call DealDamage(caster, target, GetDamage(level))
                call DestroyEffect(AddSpecialEffect(HIT_EFFECT, tx, ty))
                static if (DISPLAY_TEXT) then
                    call DisplayText(target, COLOUR_DAMAGE + I2S(R2I(GetDamage(level))) + "!|r")
                endif
            // Target is outside area:
            else
                // Target is within max distance:
                if (distance <= MAX_DISTANCE) then
                    call ManaMissile.create(tx, ty, target, ((MAX_DISTANCE - distance) / MAX_DISTANCE) * GetManaBurn(level))
                endif
                call DestroyEffect(AddSpecialEffect(MISS_EFFECT, tx, ty))
            endif
           
            call destroy()
        endmethod
       
        /**
         * Displays the estimated area.
         */

        private method displayArea takes nothing returns nothing
            local player owner = GetOwningPlayer(caster)
            local integer i = 0
           
            set fx = fxList.create()
           
            loop
                exitwhen i == NUM_EFFECTS
                set fx[i] = Effect.create(owner, tx + area * Cos(i * ANGLE_DIV), ty + area * Sin(i * ANGLE_DIV), /*
                    */
EFFECT_SCALE, EFFECT_HEIGHT, INDICATOR_ART)
                set i = i + 1
            endloop
           
            set locFx = Effect.create(owner, tx, ty, L_FX_SCALE, L_FX_HEIGHT, INDICATOR_FX)
                   
            set owner = null
        endmethod
       
        /**
         * Checks whether the target moved. Creates the estimated area accordingly.
         */

        private static method checkPosition takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real newX
            local real newY
            local real angle
            local real distance
           
            set newX = GetUnitX(target)
            set newY = GetUnitY(target)
           
            // Target has moved:
            if (newX != tx and newY != ty) then
                set angle = Atan2(tx - newX, ty - newY)
                set distance = SquareRoot((newX - tx) * (newX - tx) + (newY - ty) * (newY - ty)) / INITIAL_DELAY * GetDelay(level)
                set tx = tx + distance * Cos(angle)
                set ty = ty + distance * Sin(angle)
            endif
           
            call displayArea()
            call TimerStart(GetExpiredTimer(), GetDelay(level), false, function thistype.explode)
        endmethod
       
        /**
         * Allocate a new instance and save the target's position. Start a timer to check
         * whether it is moving.
         */

        private static method create takes nothing returns thistype
            local thistype this
           
            if (numRecycles == 0) then
                set numInstances = numInstances + 1
                set this = numInstances
            else
                set numRecycles = numRecycles - 1
                set this = recycled[numRecycles]
            endif
           
            set caster = GetTriggerUnit()
            set level = GetUnitAbilityLevel(caster, SPELL_ID)
            set area = GetArea(level)
            set target = GetSpellTargetUnit()
            set tx = GetUnitX(target)
            set ty = GetUnitY(target)
            call TimerStart(NewTimerEx(this), INITIAL_DELAY, false, function thistype.checkPosition)
           
            return this
        endmethod
   
        static if (not LIBRARY_SpellEffectEvent) then
            private static method checkSpell takes nothing returns boolean
                if (GetSpellAbilityId() == SPELL_ID) then
                    call create()
                endif
                return false
            endmethod
        endif
       
        /**
         * Register the spell and preload effects.
         */

        private static method onInit takes nothing returns nothing
            static if (LIBRARY_SpellEffectEvent) then
                call RegisterSpellEffectEvent(SPELL_ID, function thistype.create)
            else
                local trigger t = CreateTrigger()
                call TriggerAddCondition(t, Condition(function thistype.checkSpell))
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            endif
           
            call Preload(INDICATOR_FX)
            call Preload(INDICATOR_ART)
            call Preload(EXPLODE_EFFECT)
            call Preload(HIT_EFFECT)
            call Preload(MISS_EFFECT)
            call Preload(MISSILE_ART)
            call Preload(MANA_BURN_FX)
        endmethod
   
    endstruct

endlibrary

Changelog


v1.0
- Initial release.

Credits


For Systems:
- Bribe
- Vexorian

Keywords:
mr_bean, prediction, mind, guess, predict
Contents

Prediction (Map)

Reviews
Moderator
00:14, 23rd Jun 2013 PurgeandFire: Review: http://www.hiveworkshop.com/forums/2365603-post6.html Approved, though, since those updates are incredibly minor.
  1. 00:14, 23rd Jun 2013
    PurgeandFire: Review:
    http://www.hiveworkshop.com/forums/2365603-post6.html

    Approved, though, since those updates are incredibly minor.
     
  2. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Beautiful documentation ;D
     
  3. Mr_Bean

    Mr_Bean

    Joined:
    Feb 11, 2011
    Messages:
    1,823
    Resources:
    6
    Maps:
    2
    Spells:
    4
    Resources:
    6
    Haha, thanks. I think it's pretty clear that I got the idea from you :)
     
  4. msongyboi

    msongyboi

    Joined:
    Feb 3, 2013
    Messages:
    271
    Resources:
    4
    Spells:
    4
    Resources:
    4
    Pretty awesome :)
     
  5. MasterTrainer

    MasterTrainer

    Joined:
    Apr 14, 2012
    Messages:
    2,769
    Resources:
    3
    Icons:
    1
    Maps:
    1
    Spells:
    1
    Resources:
    3
    I like the concept a lot :D
     
  6. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    In the method "checkPosition", you can just set the values of newX and newY on declaration (local real newX = GetUnitX(target))

    Otherwise the code looks nice. I don't remember the internal code behind defining types that extend arrays but eh, I suppose it should be okay.
     
  7. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,526
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    u can get rid of bj_degtorad and bj_radtodeg and the bj_pi when used for angles.
     
  8. stonneash

    stonneash

    Joined:
    Jul 20, 2011
    Messages:
    1,112
    Resources:
    50
    Models:
    20
    Icons:
    29
    Skins:
    1
    Resources:
    50
    Amazing spell, and unique idea too bad I suck at jass so I can't review it.
    Good job
     
  9. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    You can do this by just setting fxpath differently when checking
    GetLocalPlayer
    .
    Example:
    Code (vJASS):
    local xefx fx = xefx.create(0, 0, 0)
    local string path

    if GetLocalPlayer() == Player(0) then
        set path = "units\\human\\Peasant\\Peasant.mdl"
    else
        set path = ""
    endif

    set fx.fxpath = path